Skip to main content

nautilus_architect_ax/http/
query.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2026 Nautech Systems Pty Ltd. All rights reserved.
3//  https://nautechsystems.io
4//
5//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6//  You may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16//! Request parameter structures for the Ax REST API.
17//!
18//! Each struct corresponds to an Ax REST endpoint and is annotated
19//! using `serde` so that it can be serialized directly into the query string
20//! or request body expected by the exchange.
21//!
22//! Parameter structs are built using the builder pattern and then passed to
23//! `AxRawHttpClient` methods where they are automatically serialized.
24
25use serde::{Deserialize, Serialize};
26use ustr::Ustr;
27
28use crate::common::enums::{AxCandleWidth, AxOrderStatus};
29
30/// Parameters for the GET /ticker endpoint.
31///
32/// # References
33/// - <https://docs.architect.exchange/api-reference/marketdata/get-ticker>
34#[derive(Clone, Debug, Deserialize, Serialize)]
35pub struct GetTickerParams {
36    /// Instrument symbol, e.g. "GBPUSD-PERP", "EURUSD-PERP".
37    pub symbol: Ustr,
38}
39
40impl GetTickerParams {
41    /// Creates a new [`GetTickerParams`] with the given symbol.
42    #[must_use]
43    pub fn new(symbol: Ustr) -> Self {
44        Self { symbol }
45    }
46}
47
48/// Parameters for the GET /instrument endpoint.
49///
50/// # References
51/// - <https://docs.architect.exchange/api-reference/symbols-instruments/get-instrument>
52#[derive(Clone, Debug, Deserialize, Serialize)]
53pub struct GetInstrumentParams {
54    /// Instrument symbol, e.g. "GBPUSD-PERP", "EURUSD-PERP".
55    pub symbol: Ustr,
56}
57
58impl GetInstrumentParams {
59    /// Creates a new [`GetInstrumentParams`] with the given symbol.
60    #[must_use]
61    pub fn new(symbol: Ustr) -> Self {
62        Self { symbol }
63    }
64}
65
66/// Parameters for the GET /candles endpoint.
67///
68/// # References
69/// - <https://docs.architect.exchange/api-reference/marketdata/get-candles>
70#[derive(Clone, Debug, Deserialize, Serialize)]
71pub struct GetCandlesParams {
72    /// Instrument symbol.
73    pub symbol: Ustr,
74    /// Start timestamp in nanoseconds.
75    pub start_timestamp_ns: i64,
76    /// End timestamp in nanoseconds.
77    pub end_timestamp_ns: i64,
78    /// Candle width/interval.
79    pub candle_width: AxCandleWidth,
80}
81
82impl GetCandlesParams {
83    /// Creates a new [`GetCandlesParams`].
84    #[must_use]
85    pub fn new(
86        symbol: Ustr,
87        start_timestamp_ns: i64,
88        end_timestamp_ns: i64,
89        candle_width: AxCandleWidth,
90    ) -> Self {
91        Self {
92            symbol,
93            start_timestamp_ns,
94            end_timestamp_ns,
95            candle_width,
96        }
97    }
98}
99
100/// Parameters for the GET /candles/current and GET /candles/last endpoints.
101///
102/// # References
103/// - <https://docs.architect.exchange/api-reference/marketdata/get-current-candle>
104/// - <https://docs.architect.exchange/api-reference/marketdata/get-last-candle>
105#[derive(Clone, Debug, Deserialize, Serialize)]
106pub struct GetCandleParams {
107    /// Instrument symbol.
108    pub symbol: Ustr,
109    /// Candle width/interval.
110    pub candle_width: AxCandleWidth,
111}
112
113impl GetCandleParams {
114    /// Creates a new [`GetCandleParams`].
115    #[must_use]
116    pub fn new(symbol: Ustr, candle_width: AxCandleWidth) -> Self {
117        Self {
118            symbol,
119            candle_width,
120        }
121    }
122}
123
124/// Parameters for the GET /funding-rates endpoint.
125///
126/// # References
127/// - <https://docs.architect.exchange/api-reference/marketdata/get-funding-rates>
128#[derive(Clone, Debug, Deserialize, Serialize)]
129pub struct GetFundingRatesParams {
130    /// Instrument symbol.
131    pub symbol: Ustr,
132    /// Start timestamp in nanoseconds.
133    pub start_timestamp_ns: i64,
134    /// End timestamp in nanoseconds.
135    pub end_timestamp_ns: i64,
136}
137
138impl GetFundingRatesParams {
139    /// Creates a new [`GetFundingRatesParams`].
140    #[must_use]
141    pub fn new(symbol: Ustr, start_timestamp_ns: i64, end_timestamp_ns: i64) -> Self {
142        Self {
143            symbol,
144            start_timestamp_ns,
145            end_timestamp_ns,
146        }
147    }
148}
149
150/// Parameters for the GET /fills endpoint.
151///
152/// # References
153/// - <https://docs.architect.exchange/api-reference/order-management/get-order-fills>
154#[derive(Clone, Debug, Deserialize, Serialize)]
155pub struct GetFillsParams {
156    /// Start timestamp in nanoseconds.
157    pub start_timestamp_ns: i64,
158    /// End timestamp in nanoseconds.
159    pub end_timestamp_ns: i64,
160}
161
162impl GetFillsParams {
163    /// Creates a new [`GetFillsParams`].
164    #[must_use]
165    pub fn new(start_timestamp_ns: i64, end_timestamp_ns: i64) -> Self {
166        Self {
167            start_timestamp_ns,
168            end_timestamp_ns,
169        }
170    }
171}
172
173/// Parameters for the GET /transactions endpoint.
174///
175/// # References
176/// - <https://docs.architect.exchange/api-reference/portfolio-management/get-transactions>
177#[derive(Clone, Debug, Deserialize, Serialize)]
178pub struct GetTransactionsParams {
179    /// Transaction types to filter by.
180    pub transaction_types: Vec<String>,
181}
182
183impl GetTransactionsParams {
184    /// Creates a new [`GetTransactionsParams`].
185    #[must_use]
186    pub fn new(transaction_types: Vec<String>) -> Self {
187        Self { transaction_types }
188    }
189}
190
191/// Parameters for the GET /trades endpoint.
192///
193/// # References
194/// - <https://docs.architect.exchange/api-reference/market-data/get-trades>
195#[derive(Clone, Debug, Deserialize, Serialize)]
196pub struct GetTradesParams {
197    /// Instrument symbol, e.g. "BTC-PERP".
198    pub symbol: Ustr,
199    /// Maximum number of trades to return (max 100, default 10).
200    #[serde(skip_serializing_if = "Option::is_none")]
201    pub limit: Option<i32>,
202}
203
204impl GetTradesParams {
205    /// Creates a new [`GetTradesParams`].
206    #[must_use]
207    pub fn new(symbol: Ustr, limit: Option<i32>) -> Self {
208        Self { symbol, limit }
209    }
210}
211
212/// Parameters for the GET /book endpoint.
213///
214/// # References
215/// - <https://docs.architect.exchange/api-reference/market-data/get-book>
216#[derive(Clone, Debug, Deserialize, Serialize)]
217pub struct GetBookParams {
218    /// Instrument symbol, e.g. "BTC-PERP".
219    pub symbol: Ustr,
220    /// Book depth level: 2 (aggregated) or 3 (individual orders). Defaults to 2.
221    #[serde(skip_serializing_if = "Option::is_none")]
222    pub level: Option<i32>,
223}
224
225impl GetBookParams {
226    /// Creates a new [`GetBookParams`].
227    #[must_use]
228    pub fn new(symbol: Ustr, level: Option<i32>) -> Self {
229        Self { symbol, level }
230    }
231}
232
233/// Parameters for the GET /order-status endpoint.
234///
235/// Exactly one of `oid` or `cid` must be provided.
236///
237/// # References
238/// - <https://docs.architect.exchange/api-reference/order-management/get-order-status>
239#[derive(Clone, Debug, Deserialize, Serialize)]
240pub struct GetOrderStatusParams {
241    /// Order ID (e.g. "O-01ARZ3NDEKTSV4RRFFQ69G5FAV").
242    #[serde(rename = "oid", skip_serializing_if = "Option::is_none")]
243    pub order_id: Option<String>,
244    /// Client order ID (64-bit integer).
245    #[serde(rename = "cid", skip_serializing_if = "Option::is_none")]
246    pub client_order_id: Option<u64>,
247}
248
249impl GetOrderStatusParams {
250    /// Creates params to look up by venue order ID.
251    #[must_use]
252    pub fn by_order_id(order_id: impl Into<String>) -> Self {
253        Self {
254            order_id: Some(order_id.into()),
255            client_order_id: None,
256        }
257    }
258
259    /// Creates params to look up by client order ID.
260    #[must_use]
261    pub fn by_client_order_id(cid: u64) -> Self {
262        Self {
263            order_id: None,
264            client_order_id: Some(cid),
265        }
266    }
267}
268
269/// Parameters for the GET /orders endpoint.
270///
271/// # References
272/// - <https://docs.architect.exchange/api-reference/order-management/get-orders>
273#[derive(Clone, Debug, Default, Deserialize, Serialize)]
274pub struct GetOrdersParams {
275    /// Filter by trading symbol.
276    #[serde(skip_serializing_if = "Option::is_none")]
277    pub symbol: Option<Ustr>,
278    /// Beginning of time range (ISO 8601).
279    #[serde(skip_serializing_if = "Option::is_none")]
280    pub start_time: Option<String>,
281    /// End of time range (ISO 8601).
282    #[serde(skip_serializing_if = "Option::is_none")]
283    pub end_time: Option<String>,
284    /// Maximum results returned.
285    #[serde(skip_serializing_if = "Option::is_none")]
286    pub limit: Option<i32>,
287    /// Pagination offset.
288    #[serde(skip_serializing_if = "Option::is_none")]
289    pub offset: Option<i32>,
290    /// Filter by order state.
291    #[serde(skip_serializing_if = "Option::is_none")]
292    pub order_state: Option<AxOrderStatus>,
293}
294
295impl GetOrdersParams {
296    /// Creates a new empty [`GetOrdersParams`].
297    #[must_use]
298    pub fn new() -> Self {
299        Self::default()
300    }
301}
302
303#[cfg(test)]
304mod tests {
305    use rstest::rstest;
306    use ustr::Ustr;
307
308    use super::*;
309
310    #[rstest]
311    fn test_get_ticker_params_serialization() {
312        let params = GetTickerParams::new(Ustr::from("GBPUSD-PERP"));
313        let qs = serde_urlencoded::to_string(&params).unwrap();
314        assert_eq!(qs, "symbol=GBPUSD-PERP");
315    }
316
317    #[rstest]
318    fn test_get_instrument_params_serialization() {
319        let params = GetInstrumentParams::new(Ustr::from("EURUSD-PERP"));
320        let qs = serde_urlencoded::to_string(&params).unwrap();
321        assert_eq!(qs, "symbol=EURUSD-PERP");
322    }
323
324    #[rstest]
325    fn test_get_candles_params_serialization() {
326        let params = GetCandlesParams::new(
327            Ustr::from("GBPUSD-PERP"),
328            1000000000,
329            2000000000,
330            AxCandleWidth::Minutes1,
331        );
332        let qs = serde_urlencoded::to_string(&params).unwrap();
333        assert!(qs.contains("symbol=GBPUSD-PERP"));
334        assert!(qs.contains("start_timestamp_ns=1000000000"));
335        assert!(qs.contains("end_timestamp_ns=2000000000"));
336        assert!(qs.contains("candle_width=1m"));
337    }
338
339    #[rstest]
340    fn test_get_candle_params_serialization() {
341        let params = GetCandleParams::new(Ustr::from("GBPUSD-PERP"), AxCandleWidth::Hours1);
342        let qs = serde_urlencoded::to_string(&params).unwrap();
343        assert!(qs.contains("symbol=GBPUSD-PERP"));
344        assert!(qs.contains("candle_width=1h"));
345    }
346
347    #[rstest]
348    fn test_get_funding_rates_params_serialization() {
349        let params = GetFundingRatesParams::new(Ustr::from("GBPUSD-PERP"), 1000000000, 2000000000);
350        let qs = serde_urlencoded::to_string(&params).unwrap();
351        assert!(qs.contains("symbol=GBPUSD-PERP"));
352        assert!(qs.contains("start_timestamp_ns=1000000000"));
353        assert!(qs.contains("end_timestamp_ns=2000000000"));
354    }
355
356    #[rstest]
357    fn test_get_fills_params_serialization() {
358        let params = GetFillsParams::new(1000000000, 2000000000);
359        let qs = serde_urlencoded::to_string(&params).unwrap();
360        assert!(qs.contains("start_timestamp_ns=1000000000"));
361        assert!(qs.contains("end_timestamp_ns=2000000000"));
362    }
363
364    #[rstest]
365    fn test_get_trades_params_serialization() {
366        let params = GetTradesParams::new(Ustr::from("BTC-PERP"), Some(50));
367        let qs = serde_urlencoded::to_string(&params).unwrap();
368        assert!(qs.contains("symbol=BTC-PERP"));
369        assert!(qs.contains("limit=50"));
370    }
371
372    #[rstest]
373    fn test_get_trades_params_serialization_no_limit() {
374        let params = GetTradesParams::new(Ustr::from("BTC-PERP"), None);
375        let qs = serde_urlencoded::to_string(&params).unwrap();
376        assert_eq!(qs, "symbol=BTC-PERP");
377    }
378
379    #[rstest]
380    fn test_get_book_params_serialization() {
381        let params = GetBookParams::new(Ustr::from("EURUSD-PERP"), Some(3));
382        let qs = serde_urlencoded::to_string(&params).unwrap();
383        assert!(qs.contains("symbol=EURUSD-PERP"));
384        assert!(qs.contains("level=3"));
385    }
386
387    #[rstest]
388    fn test_get_order_status_by_order_id_serialization() {
389        let params = GetOrderStatusParams::by_order_id("O-01ARZ3NDEKTSV4RRFFQ69G5FAV");
390        let qs = serde_urlencoded::to_string(&params).unwrap();
391        assert_eq!(qs, "oid=O-01ARZ3NDEKTSV4RRFFQ69G5FAV");
392    }
393
394    #[rstest]
395    fn test_get_order_status_by_client_order_id_serialization() {
396        let params = GetOrderStatusParams::by_client_order_id(12345);
397        let qs = serde_urlencoded::to_string(&params).unwrap();
398        assert_eq!(qs, "cid=12345");
399    }
400}