Skip to main content

finance_query/adapters/polygon/
models.rs

1//! Shared types for Polygon.io API responses.
2
3use serde::{Deserialize, Serialize};
4
5// ============================================================================
6// Pagination and response envelope
7// ============================================================================
8
9/// Generic paginated response envelope from Polygon.
10#[derive(Debug, Clone, Serialize, Deserialize)]
11#[non_exhaustive]
12pub struct PaginatedResponse<T> {
13    /// Result items (may be absent on empty responses).
14    pub results: Option<Vec<T>>,
15    /// Response status (e.g., `"OK"`, `"DELAYED"`).
16    pub status: Option<String>,
17    /// Unique request identifier.
18    pub request_id: Option<String>,
19    /// Number of results in this page.
20    #[serde(rename = "resultsCount")]
21    pub results_count: Option<usize>,
22    /// Total count across all pages.
23    pub count: Option<usize>,
24    /// Cursor URL for the next page of results.
25    pub next_url: Option<String>,
26    /// Ticker symbol (present on some endpoints).
27    pub ticker: Option<String>,
28    /// Whether results are adjusted for splits.
29    pub adjusted: Option<bool>,
30    /// Query count (aggregates).
31    #[serde(rename = "queryCount")]
32    pub query_count: Option<usize>,
33}
34
35// ============================================================================
36// Enums
37// ============================================================================
38
39/// Timespan unit for aggregate bar requests.
40#[derive(Debug, Clone, Copy, PartialEq, Eq)]
41pub enum Timespan {
42    /// 1 second
43    Second,
44    /// 1 minute
45    Minute,
46    /// 1 hour
47    Hour,
48    /// 1 day
49    Day,
50    /// 1 week
51    Week,
52    /// 1 month
53    Month,
54    /// 1 quarter
55    Quarter,
56    /// 1 year
57    Year,
58}
59
60impl Timespan {
61    /// Convert to Polygon API parameter string.
62    pub fn as_str(&self) -> &'static str {
63        match self {
64            Self::Second => "second",
65            Self::Minute => "minute",
66            Self::Hour => "hour",
67            Self::Day => "day",
68            Self::Week => "week",
69            Self::Month => "month",
70            Self::Quarter => "quarter",
71            Self::Year => "year",
72        }
73    }
74}
75
76/// Sort direction for paginated results.
77#[derive(Debug, Clone, Copy, PartialEq, Eq)]
78pub enum Sort {
79    /// Ascending (oldest first)
80    Asc,
81    /// Descending (newest first)
82    Desc,
83}
84
85impl Sort {
86    /// Convert to Polygon API parameter string.
87    pub fn as_str(&self) -> &'static str {
88        match self {
89            Self::Asc => "asc",
90            Self::Desc => "desc",
91        }
92    }
93}
94
95/// Order parameter for some endpoints.
96#[derive(Debug, Clone, Copy, PartialEq, Eq)]
97pub enum Order {
98    /// Ascending
99    Asc,
100    /// Descending
101    Desc,
102}
103
104impl Order {
105    /// Convert to Polygon API parameter string.
106    pub fn as_str(&self) -> &'static str {
107        match self {
108            Self::Asc => "asc",
109            Self::Desc => "desc",
110        }
111    }
112}
113
114// ============================================================================
115// Shared OHLCV bar (reused across stocks, options, forex, crypto, indices, futures)
116// ============================================================================
117
118/// OHLCV aggregate bar from Polygon.
119#[derive(Debug, Clone, Serialize, Deserialize)]
120#[non_exhaustive]
121pub struct AggBar {
122    /// Open price.
123    #[serde(rename = "o")]
124    pub open: f64,
125    /// High price.
126    #[serde(rename = "h")]
127    pub high: f64,
128    /// Low price.
129    #[serde(rename = "l")]
130    pub low: f64,
131    /// Close price.
132    #[serde(rename = "c")]
133    pub close: f64,
134    /// Trading volume.
135    #[serde(rename = "v")]
136    pub volume: f64,
137    /// Volume-weighted average price.
138    #[serde(rename = "vw")]
139    pub vwap: Option<f64>,
140    /// Unix millisecond timestamp of the start of the bar.
141    #[serde(rename = "t")]
142    pub timestamp: i64,
143    /// Number of transactions in this bar.
144    #[serde(rename = "n")]
145    pub transactions: Option<u64>,
146    /// Whether this bar is an OTC trade.
147    #[serde(rename = "otc")]
148    pub otc: Option<bool>,
149}
150
151/// Aggregate response with ticker metadata.
152#[derive(Debug, Clone, Serialize, Deserialize)]
153#[non_exhaustive]
154pub struct AggregateResponse {
155    /// Ticker symbol.
156    pub ticker: Option<String>,
157    /// Response status.
158    pub status: Option<String>,
159    /// Whether results are adjusted for splits.
160    pub adjusted: Option<bool>,
161    /// Number of results in this response.
162    #[serde(rename = "resultsCount")]
163    pub results_count: Option<usize>,
164    /// Query count.
165    #[serde(rename = "queryCount")]
166    pub query_count: Option<usize>,
167    /// Request identifier.
168    pub request_id: Option<String>,
169    /// Aggregate bars.
170    pub results: Option<Vec<AggBar>>,
171    /// Next page URL.
172    pub next_url: Option<String>,
173}
174
175/// Daily open/close response for a single ticker.
176#[derive(Debug, Clone, Serialize, Deserialize)]
177#[non_exhaustive]
178pub struct DailyOpenClose {
179    /// Ticker symbol.
180    pub symbol: Option<String>,
181    /// Date (YYYY-MM-DD).
182    pub from: Option<String>,
183    /// Open price.
184    pub open: Option<f64>,
185    /// High price.
186    pub high: Option<f64>,
187    /// Low price.
188    pub low: Option<f64>,
189    /// Close price.
190    pub close: Option<f64>,
191    /// Volume.
192    pub volume: Option<f64>,
193    /// After-hours price.
194    #[serde(rename = "afterHours")]
195    pub after_hours: Option<f64>,
196    /// Pre-market price.
197    #[serde(rename = "preMarket")]
198    pub pre_market: Option<f64>,
199}
200
201// ============================================================================
202// Trade and quote types
203// ============================================================================
204
205/// A single trade.
206#[derive(Debug, Clone, Serialize, Deserialize)]
207#[non_exhaustive]
208pub struct Trade {
209    /// Trade conditions.
210    pub conditions: Option<Vec<i32>>,
211    /// Trade correction indicator.
212    pub correction: Option<i32>,
213    /// Exchange ID.
214    pub exchange: Option<i32>,
215    /// Trade ID.
216    pub id: Option<String>,
217    /// Participant timestamp (nanoseconds).
218    pub participant_timestamp: Option<i64>,
219    /// Price.
220    pub price: Option<f64>,
221    /// Sequence number.
222    pub sequence_number: Option<i64>,
223    /// SIP timestamp (nanoseconds).
224    pub sip_timestamp: Option<i64>,
225    /// Trade size.
226    pub size: Option<f64>,
227    /// Tape.
228    pub tape: Option<i32>,
229    /// TRF ID.
230    pub trf_id: Option<i32>,
231    /// TRF timestamp.
232    pub trf_timestamp: Option<i64>,
233}
234
235/// A single last-trade response.
236#[derive(Debug, Clone, Serialize, Deserialize)]
237#[non_exhaustive]
238pub struct LastTrade {
239    /// Ticker symbol.
240    #[serde(rename = "T")]
241    pub ticker: Option<String>,
242    /// Conditions.
243    pub conditions: Option<Vec<i32>>,
244    /// Correction.
245    pub correction: Option<i32>,
246    /// Exchange ID.
247    pub exchange: Option<i32>,
248    /// Trade ID.
249    pub id: Option<String>,
250    /// Participant timestamp.
251    pub participant_timestamp: Option<i64>,
252    /// Price.
253    pub price: Option<f64>,
254    /// Sequence number.
255    pub sequence_number: Option<i64>,
256    /// SIP timestamp.
257    pub sip_timestamp: Option<i64>,
258    /// Size.
259    pub size: Option<f64>,
260    /// Tape.
261    pub tape: Option<i32>,
262    /// TRF ID.
263    pub trf_id: Option<i32>,
264    /// TRF timestamp.
265    pub trf_timestamp: Option<i64>,
266}
267
268/// NBBO quote.
269#[derive(Debug, Clone, Serialize, Deserialize)]
270#[non_exhaustive]
271pub struct Quote {
272    /// Ask exchange.
273    pub ask_exchange: Option<i32>,
274    /// Ask price.
275    pub ask_price: Option<f64>,
276    /// Ask size.
277    pub ask_size: Option<f64>,
278    /// Bid exchange.
279    pub bid_exchange: Option<i32>,
280    /// Bid price.
281    pub bid_price: Option<f64>,
282    /// Bid size.
283    pub bid_size: Option<f64>,
284    /// Conditions.
285    pub conditions: Option<Vec<i32>>,
286    /// Indicators.
287    pub indicators: Option<Vec<i32>>,
288    /// Participant timestamp.
289    pub participant_timestamp: Option<i64>,
290    /// Sequence number.
291    pub sequence_number: Option<i64>,
292    /// SIP timestamp.
293    pub sip_timestamp: Option<i64>,
294    /// Tape.
295    pub tape: Option<i32>,
296    /// TRF timestamp.
297    pub trf_timestamp: Option<i64>,
298}
299
300/// Last quote (NBBO) response wrapper.
301#[derive(Debug, Clone, Serialize, Deserialize)]
302#[non_exhaustive]
303pub struct LastQuoteResponse {
304    /// Request ID.
305    pub request_id: Option<String>,
306    /// Response status.
307    pub status: Option<String>,
308    /// The last quote.
309    pub results: Option<Quote>,
310}
311
312/// Last trade response wrapper.
313#[derive(Debug, Clone, Serialize, Deserialize)]
314#[non_exhaustive]
315pub struct LastTradeResponse {
316    /// Request ID.
317    pub request_id: Option<String>,
318    /// Response status.
319    pub status: Option<String>,
320    /// The last trade.
321    pub results: Option<LastTrade>,
322}
323
324// ============================================================================
325// Snapshot types
326// ============================================================================
327
328/// Day aggregate data within a snapshot.
329#[derive(Debug, Clone, Serialize, Deserialize)]
330#[non_exhaustive]
331pub struct SnapshotAgg {
332    /// Open price.
333    #[serde(rename = "o")]
334    pub open: Option<f64>,
335    /// High price.
336    #[serde(rename = "h")]
337    pub high: Option<f64>,
338    /// Low price.
339    #[serde(rename = "l")]
340    pub low: Option<f64>,
341    /// Close price.
342    #[serde(rename = "c")]
343    pub close: Option<f64>,
344    /// Volume.
345    #[serde(rename = "v")]
346    pub volume: Option<f64>,
347    /// VWAP.
348    #[serde(rename = "vw")]
349    pub vwap: Option<f64>,
350    /// Timestamp.
351    #[serde(rename = "t")]
352    pub timestamp: Option<i64>,
353    /// Transactions.
354    #[serde(rename = "n")]
355    pub transactions: Option<u64>,
356}
357
358/// A single ticker snapshot.
359#[derive(Debug, Clone, Serialize, Deserialize)]
360#[non_exhaustive]
361pub struct TickerSnapshot {
362    /// Ticker symbol.
363    pub ticker: Option<String>,
364    /// Today's change amount.
365    #[serde(rename = "todaysChange")]
366    pub todays_change: Option<f64>,
367    /// Today's change percent.
368    #[serde(rename = "todaysChangePerc")]
369    pub todays_change_perc: Option<f64>,
370    /// Updated timestamp (nanoseconds).
371    pub updated: Option<i64>,
372    /// Current day aggregate.
373    pub day: Option<SnapshotAgg>,
374    /// Previous day aggregate.
375    #[serde(rename = "prevDay")]
376    pub prev_day: Option<SnapshotAgg>,
377    /// Last trade.
378    #[serde(rename = "lastTrade")]
379    pub last_trade: Option<LastTrade>,
380    /// Last quote.
381    #[serde(rename = "lastQuote")]
382    pub last_quote: Option<Quote>,
383    /// Minute aggregate.
384    pub min: Option<SnapshotAgg>,
385}
386
387/// Snapshot response for a single ticker.
388#[derive(Debug, Clone, Serialize, Deserialize)]
389#[non_exhaustive]
390pub struct SingleSnapshotResponse {
391    /// Request ID.
392    pub request_id: Option<String>,
393    /// Response status.
394    pub status: Option<String>,
395    /// Snapshot data.
396    pub ticker: Option<TickerSnapshot>,
397}
398
399/// Snapshot response for multiple tickers.
400#[derive(Debug, Clone, Serialize, Deserialize)]
401#[non_exhaustive]
402pub struct SnapshotsResponse {
403    /// Response status.
404    pub status: Option<String>,
405    /// Request ID.
406    pub request_id: Option<String>,
407    /// Number of results.
408    pub count: Option<usize>,
409    /// Ticker snapshots.
410    pub tickers: Option<Vec<TickerSnapshot>>,
411}
412
413// ============================================================================
414// Technical indicator types
415// ============================================================================
416
417/// A single technical indicator data point.
418#[derive(Debug, Clone, Serialize, Deserialize)]
419#[non_exhaustive]
420pub struct IndicatorValue {
421    /// Timestamp.
422    pub timestamp: Option<i64>,
423    /// Indicator value.
424    pub value: Option<f64>,
425    /// Signal value (MACD).
426    pub signal: Option<f64>,
427    /// Histogram value (MACD).
428    pub histogram: Option<f64>,
429}
430
431/// Technical indicator response.
432#[derive(Debug, Clone, Serialize, Deserialize)]
433#[non_exhaustive]
434pub struct IndicatorResponse {
435    /// Response status.
436    pub status: Option<String>,
437    /// Request ID.
438    pub request_id: Option<String>,
439    /// Indicator results.
440    pub results: Option<IndicatorResults>,
441    /// Next page URL.
442    pub next_url: Option<String>,
443}
444
445/// Nested indicator results containing underlying data and values.
446#[derive(Debug, Clone, Serialize, Deserialize)]
447#[non_exhaustive]
448pub struct IndicatorResults {
449    /// Underlying aggregate bars.
450    pub underlying: Option<IndicatorUnderlying>,
451    /// Indicator values.
452    pub values: Option<Vec<IndicatorValue>>,
453}
454
455/// Underlying data for indicator responses.
456#[derive(Debug, Clone, Serialize, Deserialize)]
457#[non_exhaustive]
458pub struct IndicatorUnderlying {
459    /// Underlying aggregate bars URL.
460    pub url: Option<String>,
461    /// Underlying aggregates.
462    pub aggregates: Option<Vec<AggBar>>,
463}
464
465// ============================================================================
466// Optional aggregate parameters
467// ============================================================================
468
469/// Optional parameters for aggregate bar requests.
470#[derive(Debug, Clone, Default)]
471pub struct AggregateParams {
472    /// Whether results are adjusted for splits. Default: true.
473    pub adjusted: Option<bool>,
474    /// Sort direction.
475    pub sort: Option<Sort>,
476    /// Maximum number of results. Default: 5000, max: 50000.
477    pub limit: Option<u32>,
478}