Skip to main content

polyoxide_gamma/api/
markets.rs

1use polyoxide_core::{ApiError, HttpClient, QueryBuilder, Request, RequestError};
2
3use crate::{
4    error::GammaError,
5    types::{KeysetMarketsResponse, Market, MarketDescription, MarketsInformationBody, Tag},
6};
7
8/// Markets namespace for market-related operations
9#[derive(Clone)]
10pub struct Markets {
11    pub(crate) http_client: HttpClient,
12}
13
14impl Markets {
15    /// Get a specific market by ID
16    pub fn get(&self, id: impl Into<String>) -> GetMarket {
17        GetMarket {
18            request: Request::new(
19                self.http_client.clone(),
20                format!("/markets/{}", urlencoding::encode(&id.into())),
21            ),
22        }
23    }
24
25    /// Get a market by its slug
26    pub fn get_by_slug(&self, slug: impl Into<String>) -> GetMarket {
27        GetMarket {
28            request: Request::new(
29                self.http_client.clone(),
30                format!("/markets/slug/{}", urlencoding::encode(&slug.into())),
31            ),
32        }
33    }
34
35    /// List markets with optional filtering
36    pub fn list(&self) -> ListMarkets {
37        ListMarkets {
38            request: Request::new(self.http_client.clone(), "/markets"),
39        }
40    }
41
42    /// Look up markets by ID, returning both open and closed markets.
43    ///
44    /// Unlike [`Self::list`] with `.id(…)`, which inherits the upstream
45    /// `closed=false` default and silently drops closed markets, `get_many`
46    /// issues two parallel requests (`closed=true` and `closed=false`) and
47    /// merges the results, so callers get every matching market regardless of
48    /// status. This matches the semantics of the single-market [`Self::get`]
49    /// endpoint, batched.
50    ///
51    /// Safe batch size: ≤ 400 IDs per call (same URL-length ceiling as
52    /// [`ListMarkets::id`]). The two fan-out requests are issued concurrently,
53    /// so wall-clock latency is one round-trip, not two.
54    pub fn get_many(&self, ids: impl IntoIterator<Item = i64>) -> GetManyMarkets {
55        GetManyMarkets {
56            http_client: self.http_client.clone(),
57            ids: ids.into_iter().collect(),
58            include_tag: None,
59        }
60    }
61
62    /// Get tags for a market
63    pub fn tags(&self, id: impl Into<String>) -> Request<Vec<Tag>, GammaError> {
64        Request::new(
65            self.http_client.clone(),
66            format!("/markets/{}/tags", urlencoding::encode(&id.into())),
67        )
68    }
69
70    /// Get a market's description text (`GET /markets/{id}/description`).
71    pub fn get_description(&self, id: impl Into<String>) -> Request<MarketDescription, GammaError> {
72        Request::new(
73            self.http_client.clone(),
74            format!("/markets/{}/description", urlencoding::encode(&id.into())),
75        )
76    }
77
78    /// Query markets by an information filter body (`POST /markets/information`).
79    ///
80    /// Unlike [`Self::list`], the filter parameters are passed in the request
81    /// body rather than the query string, allowing larger batches of IDs,
82    /// slugs, token IDs, etc. without hitting URL-length limits.
83    pub async fn query_by_information(
84        &self,
85        body: &MarketsInformationBody,
86    ) -> Result<Vec<Market>, GammaError> {
87        post_json(&self.http_client, "/markets/information", body).await
88    }
89
90    /// Query abridged markets by an information filter body
91    /// (`POST /markets/abridged`). Returns a reduced-payload market list.
92    pub async fn query_abridged(
93        &self,
94        body: &MarketsInformationBody,
95    ) -> Result<Vec<Market>, GammaError> {
96        post_json(&self.http_client, "/markets/abridged", body).await
97    }
98
99    /// List markets using cursor-based (keyset) pagination
100    /// (`GET /markets/keyset`).
101    ///
102    /// Prefer this over [`Self::list`] for stable paging through large result
103    /// sets. Use `next_cursor` from each response as `after_cursor` in the
104    /// next request; pagination is complete when `next_cursor` is `None`.
105    pub fn list_keyset(&self) -> ListKeysetMarkets {
106        ListKeysetMarkets {
107            request: Request::new(self.http_client.clone(), "/markets/keyset"),
108        }
109    }
110}
111
112/// POST a JSON body and deserialize a JSON response. Shared by
113/// `query_by_information` and `query_abridged`.
114async fn post_json<B: serde::Serialize, T: serde::de::DeserializeOwned>(
115    http: &HttpClient,
116    path: &str,
117    body: &B,
118) -> Result<T, GammaError> {
119    let url = http
120        .base_url
121        .join(path)
122        .map_err(|e| GammaError::Api(ApiError::from(e)))?;
123
124    http.acquire_rate_limit(path, Some(&reqwest::Method::POST))
125        .await;
126    let _permit = http.acquire_concurrency().await;
127    let response = http
128        .client
129        .post(url)
130        .json(body)
131        .send()
132        .await
133        .map_err(|e| GammaError::Api(ApiError::from(e)))?;
134
135    if !response.status().is_success() {
136        return Err(GammaError::from_response(response).await);
137    }
138
139    let text = response
140        .text()
141        .await
142        .map_err(|e| GammaError::Api(ApiError::from(e)))?;
143    serde_json::from_str(&text).map_err(|e| GammaError::Api(ApiError::from(e)))
144}
145
146/// Request builder for getting a single market
147pub struct GetMarket {
148    request: Request<Market, GammaError>,
149}
150
151impl GetMarket {
152    /// Include tag data in response
153    pub fn include_tag(mut self, include: bool) -> Self {
154        self.request = self.request.query("include_tag", include);
155        self
156    }
157
158    /// Execute the request
159    pub async fn send(self) -> Result<Market, GammaError> {
160        self.request.send().await
161    }
162}
163
164/// Request builder for batch lookup of markets by ID.
165///
166/// On [`Self::send`] this issues two parallel `/markets` requests — one with
167/// `closed=true`, one with `closed=false` — and concatenates the results. The
168/// two upstream responses are disjoint (a market is either closed or not), so
169/// merging by concatenation is exact.
170pub struct GetManyMarkets {
171    http_client: HttpClient,
172    ids: Vec<i64>,
173    include_tag: Option<bool>,
174}
175
176impl GetManyMarkets {
177    /// Include tag data in results
178    pub fn include_tag(mut self, include: bool) -> Self {
179        self.include_tag = Some(include);
180        self
181    }
182
183    /// Execute the request.
184    ///
185    /// Returns an empty vec without hitting the network when called with no
186    /// IDs. Otherwise fans out `closed=true` and `closed=false` concurrently
187    /// and fails fast if either leg errors.
188    pub async fn send(self) -> Result<Vec<Market>, GammaError> {
189        if self.ids.is_empty() {
190            return Ok(Vec::new());
191        }
192
193        let mut req_closed: Request<Vec<Market>, GammaError> =
194            Request::new(self.http_client.clone(), "/markets")
195                .query_many("id", self.ids.iter().copied())
196                .query("closed", true);
197        let mut req_open: Request<Vec<Market>, GammaError> =
198            Request::new(self.http_client, "/markets")
199                .query_many("id", self.ids.iter().copied())
200                .query("closed", false);
201
202        if let Some(include) = self.include_tag {
203            req_closed = req_closed.query("include_tag", include);
204            req_open = req_open.query("include_tag", include);
205        }
206
207        let (mut closed_markets, open_markets) =
208            tokio::try_join!(req_closed.send(), req_open.send())?;
209        closed_markets.extend(open_markets);
210        Ok(closed_markets)
211    }
212}
213
214/// Request builder for listing markets
215pub struct ListMarkets {
216    request: Request<Vec<Market>, GammaError>,
217}
218
219impl ListMarkets {
220    /// Set maximum number of results (minimum: 0)
221    pub fn limit(mut self, limit: u32) -> Self {
222        self.request = self.request.query("limit", limit);
223        self
224    }
225
226    /// Set pagination offset (minimum: 0)
227    pub fn offset(mut self, offset: u32) -> Self {
228        self.request = self.request.query("offset", offset);
229        self
230    }
231
232    /// Set order fields (comma-separated list)
233    pub fn order(mut self, order: impl Into<String>) -> Self {
234        self.request = self.request.query("order", order.into());
235        self
236    }
237
238    /// Set sort direction
239    pub fn ascending(mut self, ascending: bool) -> Self {
240        self.request = self.request.query("ascending", ascending);
241        self
242    }
243
244    /// Filter by specific market IDs
245    ///
246    /// Safe batch size: ≤ 400 per request. URLs over ~8 KB are rejected
247    /// upstream with `414 URI Too Long`; empirically the ceiling is ~583.
248    ///
249    /// # Note on closed markets
250    ///
251    /// The upstream `/markets` endpoint applies an implicit `closed=false`
252    /// default when no `closed` param is sent, so this filter silently drops
253    /// closed markets unless `.closed(true)` is also set. For pinpoint lookup
254    /// by ID regardless of status, use [`Markets::get_many`].
255    pub fn id(mut self, ids: impl IntoIterator<Item = i64>) -> Self {
256        self.request = self.request.query_many("id", ids);
257        self
258    }
259
260    /// Filter by market slugs
261    ///
262    /// Safe batch size: ≤ 100 per request. URL length is capped at ~8 KB
263    /// upstream; slug entries vary so pick a cap based on your longest slug.
264    ///
265    /// # Note on closed markets
266    ///
267    /// Same trap as [`Self::id`]: upstream defaults to `closed=false`, so this
268    /// filter drops closed markets unless `.closed(true)` is also set.
269    pub fn slug(mut self, slugs: impl IntoIterator<Item = impl ToString>) -> Self {
270        self.request = self.request.query_many("slug", slugs);
271        self
272    }
273
274    /// Filter by CLOB token IDs
275    ///
276    /// Safe batch size: ≤ 50 per request. Token IDs are 77-digit decimals
277    /// (~90 B/entry on the wire); URLs over ~8 KB are rejected with `414`.
278    pub fn clob_token_ids(mut self, token_ids: impl IntoIterator<Item = impl ToString>) -> Self {
279        self.request = self.request.query_many("clob_token_ids", token_ids);
280        self
281    }
282
283    /// Filter by condition IDs
284    ///
285    /// Safe batch size: ≤ 60 per request. Condition IDs are 66-char hex
286    /// (~80 B/entry); empirically the upstream ceiling is exactly 100 before
287    /// `414 URI Too Long`.
288    ///
289    /// # Note on closed markets
290    ///
291    /// Same trap as [`Self::id`]: upstream defaults to `closed=false`, so this
292    /// filter drops closed markets unless `.closed(true)` is also set.
293    pub fn condition_ids(mut self, condition_ids: impl IntoIterator<Item = impl ToString>) -> Self {
294        self.request = self.request.query_many("condition_ids", condition_ids);
295        self
296    }
297
298    /// Filter by market maker addresses
299    ///
300    /// Safe batch size: ≤ 80 per request. Ethereum addresses are 42 chars
301    /// (~60 B/entry); URLs over ~8 KB are rejected upstream with `414`.
302    pub fn market_maker_address(
303        mut self,
304        addresses: impl IntoIterator<Item = impl ToString>,
305    ) -> Self {
306        self.request = self.request.query_many("market_maker_address", addresses);
307        self
308    }
309
310    /// Set minimum liquidity threshold
311    pub fn liquidity_num_min(mut self, min: f64) -> Self {
312        self.request = self.request.query("liquidity_num_min", min);
313        self
314    }
315
316    /// Set maximum liquidity threshold
317    pub fn liquidity_num_max(mut self, max: f64) -> Self {
318        self.request = self.request.query("liquidity_num_max", max);
319        self
320    }
321
322    /// Set minimum trading volume
323    pub fn volume_num_min(mut self, min: f64) -> Self {
324        self.request = self.request.query("volume_num_min", min);
325        self
326    }
327
328    /// Set maximum trading volume
329    pub fn volume_num_max(mut self, max: f64) -> Self {
330        self.request = self.request.query("volume_num_max", max);
331        self
332    }
333
334    /// Set earliest market start date (ISO 8601 format)
335    pub fn start_date_min(mut self, date: impl Into<String>) -> Self {
336        self.request = self.request.query("start_date_min", date.into());
337        self
338    }
339
340    /// Set latest market start date (ISO 8601 format)
341    pub fn start_date_max(mut self, date: impl Into<String>) -> Self {
342        self.request = self.request.query("start_date_max", date.into());
343        self
344    }
345
346    /// Set earliest market end date (ISO 8601 format)
347    pub fn end_date_min(mut self, date: impl Into<String>) -> Self {
348        self.request = self.request.query("end_date_min", date.into());
349        self
350    }
351
352    /// Set latest market end date (ISO 8601 format)
353    pub fn end_date_max(mut self, date: impl Into<String>) -> Self {
354        self.request = self.request.query("end_date_max", date.into());
355        self
356    }
357
358    /// Filter by tag identifier
359    pub fn tag_id(mut self, tag_id: i64) -> Self {
360        self.request = self.request.query("tag_id", tag_id);
361        self
362    }
363
364    /// Include related tags in response
365    pub fn related_tags(mut self, include: bool) -> Self {
366        self.request = self.request.query("related_tags", include);
367        self
368    }
369
370    /// Filter for create-your-own markets
371    pub fn cyom(mut self, cyom: bool) -> Self {
372        self.request = self.request.query("cyom", cyom);
373        self
374    }
375
376    /// Filter by UMA resolution status
377    pub fn uma_resolution_status(mut self, status: impl Into<String>) -> Self {
378        self.request = self.request.query("uma_resolution_status", status.into());
379        self
380    }
381
382    /// Filter by game identifier
383    pub fn game_id(mut self, game_id: impl Into<String>) -> Self {
384        self.request = self.request.query("game_id", game_id.into());
385        self
386    }
387
388    /// Filter by sports market types
389    ///
390    /// Safe batch size: ≤ 150 per request. URL length is capped at ~8 KB
391    /// upstream (`414 URI Too Long`).
392    pub fn sports_market_types(mut self, types: impl IntoIterator<Item = impl ToString>) -> Self {
393        self.request = self.request.query_many("sports_market_types", types);
394        self
395    }
396
397    /// Set minimum rewards threshold
398    pub fn rewards_min_size(mut self, min: f64) -> Self {
399        self.request = self.request.query("rewards_min_size", min);
400        self
401    }
402
403    /// Filter by question identifiers
404    ///
405    /// Safe batch size: ≤ 60 per request. Question IDs are 66-char hex
406    /// (~80 B/entry); URLs over ~8 KB are rejected upstream with `414`.
407    pub fn question_ids(mut self, question_ids: impl IntoIterator<Item = impl ToString>) -> Self {
408        self.request = self.request.query_many("question_ids", question_ids);
409        self
410    }
411
412    /// Include tag data in results
413    pub fn include_tag(mut self, include: bool) -> Self {
414        self.request = self.request.query("include_tag", include);
415        self
416    }
417
418    /// Filter for closed or active markets
419    pub fn closed(mut self, closed: bool) -> Self {
420        self.request = self.request.query("closed", closed);
421        self
422    }
423
424    /// Filter by open status (convenience method, opposite of closed)
425    pub fn open(mut self, open: bool) -> Self {
426        self.request = self.request.query("closed", !open);
427        self
428    }
429
430    /// Filter by archived status
431    pub fn archived(mut self, archived: bool) -> Self {
432        self.request = self.request.query("archived", archived);
433        self
434    }
435
436    /// Execute the request
437    pub async fn send(self) -> Result<Vec<Market>, GammaError> {
438        self.request.send().await
439    }
440}
441
442/// Request builder for [`Markets::list_keyset`].
443pub struct ListKeysetMarkets {
444    request: Request<KeysetMarketsResponse, GammaError>,
445}
446
447impl ListKeysetMarkets {
448    /// Maximum number of results to return (upstream max 1000).
449    pub fn limit(mut self, limit: u32) -> Self {
450        self.request = self.request.query("limit", limit);
451        self
452    }
453
454    /// Comma-separated list of JSON field names to order by.
455    pub fn order(mut self, order: impl Into<String>) -> Self {
456        self.request = self.request.query("order", order.into());
457        self
458    }
459
460    /// Sort direction (used only when `order` is set).
461    pub fn ascending(mut self, ascending: bool) -> Self {
462        self.request = self.request.query("ascending", ascending);
463        self
464    }
465
466    /// Opaque cursor token from a previous response's `next_cursor`.
467    pub fn after_cursor(mut self, cursor: impl Into<String>) -> Self {
468        self.request = self.request.query("after_cursor", cursor.into());
469        self
470    }
471
472    /// Filter by specific market IDs.
473    pub fn id(mut self, ids: impl IntoIterator<Item = i64>) -> Self {
474        self.request = self.request.query_many("id", ids);
475        self
476    }
477
478    /// Filter by market slugs.
479    pub fn slug(mut self, slugs: impl IntoIterator<Item = impl ToString>) -> Self {
480        self.request = self.request.query_many("slug", slugs);
481        self
482    }
483
484    /// Filter by closed status (defaults to `false` upstream).
485    pub fn closed(mut self, closed: bool) -> Self {
486        self.request = self.request.query("closed", closed);
487        self
488    }
489
490    /// Filter by CLOB token IDs.
491    pub fn clob_token_ids(mut self, ids: impl IntoIterator<Item = impl ToString>) -> Self {
492        self.request = self.request.query_many("clob_token_ids", ids);
493        self
494    }
495
496    /// Filter by condition IDs.
497    pub fn condition_ids(mut self, ids: impl IntoIterator<Item = impl ToString>) -> Self {
498        self.request = self.request.query_many("condition_ids", ids);
499        self
500    }
501
502    /// Filter by question IDs.
503    pub fn question_ids(mut self, ids: impl IntoIterator<Item = impl ToString>) -> Self {
504        self.request = self.request.query_many("question_ids", ids);
505        self
506    }
507
508    /// Filter by market-maker addresses.
509    pub fn market_maker_address(
510        mut self,
511        addresses: impl IntoIterator<Item = impl ToString>,
512    ) -> Self {
513        self.request = self.request.query_many("market_maker_address", addresses);
514        self
515    }
516
517    /// Set minimum liquidity threshold.
518    pub fn liquidity_num_min(mut self, min: f64) -> Self {
519        self.request = self.request.query("liquidity_num_min", min);
520        self
521    }
522
523    /// Set maximum liquidity threshold.
524    pub fn liquidity_num_max(mut self, max: f64) -> Self {
525        self.request = self.request.query("liquidity_num_max", max);
526        self
527    }
528
529    /// Set minimum trading volume.
530    pub fn volume_num_min(mut self, min: f64) -> Self {
531        self.request = self.request.query("volume_num_min", min);
532        self
533    }
534
535    /// Set maximum trading volume.
536    pub fn volume_num_max(mut self, max: f64) -> Self {
537        self.request = self.request.query("volume_num_max", max);
538        self
539    }
540
541    /// Set earliest market start date (ISO 8601 format).
542    pub fn start_date_min(mut self, date: impl Into<String>) -> Self {
543        self.request = self.request.query("start_date_min", date.into());
544        self
545    }
546
547    /// Set latest market start date (ISO 8601 format).
548    pub fn start_date_max(mut self, date: impl Into<String>) -> Self {
549        self.request = self.request.query("start_date_max", date.into());
550        self
551    }
552
553    /// Set earliest market end date (ISO 8601 format).
554    pub fn end_date_min(mut self, date: impl Into<String>) -> Self {
555        self.request = self.request.query("end_date_min", date.into());
556        self
557    }
558
559    /// Set latest market end date (ISO 8601 format).
560    pub fn end_date_max(mut self, date: impl Into<String>) -> Self {
561        self.request = self.request.query("end_date_max", date.into());
562        self
563    }
564
565    /// Filter by tag IDs.
566    pub fn tag_id(mut self, tag_ids: impl IntoIterator<Item = i64>) -> Self {
567        self.request = self.request.query_many("tag_id", tag_ids);
568        self
569    }
570
571    /// Include related tags in response.
572    pub fn related_tags(mut self, include: bool) -> Self {
573        self.request = self.request.query("related_tags", include);
574        self
575    }
576
577    /// Filter create-your-own markets.
578    pub fn cyom(mut self, cyom: bool) -> Self {
579        self.request = self.request.query("cyom", cyom);
580        self
581    }
582
583    /// Filter markets with RFQ enabled.
584    pub fn rfq_enabled(mut self, enabled: bool) -> Self {
585        self.request = self.request.query("rfq_enabled", enabled);
586        self
587    }
588
589    /// Filter by UMA resolution status.
590    pub fn uma_resolution_status(mut self, status: impl Into<String>) -> Self {
591        self.request = self.request.query("uma_resolution_status", status.into());
592        self
593    }
594
595    /// Filter by game identifier.
596    pub fn game_id(mut self, game_id: impl Into<String>) -> Self {
597        self.request = self.request.query("game_id", game_id.into());
598        self
599    }
600
601    /// Filter by sports market types.
602    pub fn sports_market_types(mut self, types: impl IntoIterator<Item = impl ToString>) -> Self {
603        self.request = self.request.query_many("sports_market_types", types);
604        self
605    }
606
607    /// Include tag data in results.
608    pub fn include_tag(mut self, include: bool) -> Self {
609        self.request = self.request.query("include_tag", include);
610        self
611    }
612
613    /// Execute the request.
614    pub async fn send(self) -> Result<KeysetMarketsResponse, GammaError> {
615        self.request.send().await
616    }
617}
618
619#[cfg(test)]
620mod tests {
621    use crate::Gamma;
622
623    fn gamma() -> Gamma {
624        Gamma::new().unwrap()
625    }
626
627    /// Verify that all builder methods chain correctly (compile-time type check)
628    /// and produce a valid builder ready to send.
629    #[test]
630    fn test_list_markets_full_chain() {
631        // This test verifies that every builder method returns Self and chains
632        let _list = gamma()
633            .markets()
634            .list()
635            .limit(25)
636            .offset(50)
637            .order("volume")
638            .ascending(false)
639            .id(vec![1i64, 2, 3])
640            .slug(vec!["slug-a"])
641            .clob_token_ids(vec!["token-1"])
642            .condition_ids(vec!["cond-1"])
643            .market_maker_address(vec!["0xaddr"])
644            .liquidity_num_min(1000.0)
645            .liquidity_num_max(50000.0)
646            .volume_num_min(100.0)
647            .volume_num_max(10000.0)
648            .start_date_min("2024-01-01")
649            .start_date_max("2025-01-01")
650            .end_date_min("2024-06-01")
651            .end_date_max("2025-12-31")
652            .tag_id(42)
653            .related_tags(true)
654            .cyom(false)
655            .uma_resolution_status("resolved")
656            .game_id("game-1")
657            .sports_market_types(vec!["moneyline"])
658            .rewards_min_size(10.0)
659            .question_ids(vec!["q1"])
660            .include_tag(true)
661            .closed(false)
662            .archived(false);
663    }
664
665    #[test]
666    fn test_open_and_closed_are_inverse() {
667        // Both should compile and produce a valid builder
668        let _open = gamma().markets().list().open(true);
669        let _closed = gamma().markets().list().closed(false);
670    }
671
672    #[test]
673    fn test_get_market_accepts_string_and_str() {
674        let _req1 = gamma().markets().get("12345");
675        let _req2 = gamma().markets().get(String::from("12345"));
676    }
677
678    #[test]
679    fn test_get_by_slug_accepts_string_and_str() {
680        let _req1 = gamma().markets().get_by_slug("my-slug");
681        let _req2 = gamma().markets().get_by_slug(String::from("my-slug"));
682    }
683
684    #[test]
685    fn test_get_market_with_include_tag() {
686        let _req = gamma().markets().get("12345").include_tag(true);
687    }
688
689    #[test]
690    fn test_market_tags_accepts_str_and_string() {
691        let _req1 = gamma().markets().tags("12345");
692        let _req2 = gamma().markets().tags(String::from("12345"));
693    }
694
695    #[test]
696    fn test_get_many_builds_with_include_tag() {
697        let _req = gamma()
698            .markets()
699            .get_many(vec![1i64, 2, 3])
700            .include_tag(true);
701    }
702
703    // ── new endpoints ───────────────────────────────────────────
704
705    #[test]
706    fn test_get_description_accepts_str_and_string() {
707        let _req1 = gamma().markets().get_description("12345");
708        let _req2 = gamma().markets().get_description(String::from("12345"));
709    }
710
711    #[test]
712    fn test_list_keyset_full_chain() {
713        let _req = gamma()
714            .markets()
715            .list_keyset()
716            .limit(50)
717            .order("volume_num,liquidity_num")
718            .ascending(false)
719            .after_cursor("opaque-cursor")
720            .id(vec![1i64, 2, 3])
721            .slug(vec!["a-slug"])
722            .closed(true)
723            .clob_token_ids(vec!["tok-a"])
724            .condition_ids(vec!["0xcond"])
725            .question_ids(vec!["q1"])
726            .market_maker_address(vec!["0xmm"])
727            .liquidity_num_min(1.0)
728            .liquidity_num_max(10.0)
729            .volume_num_min(1.0)
730            .volume_num_max(10.0)
731            .start_date_min("2024-01-01")
732            .start_date_max("2025-01-01")
733            .end_date_min("2024-01-01")
734            .end_date_max("2026-01-01")
735            .tag_id(vec![1i64, 2])
736            .related_tags(true)
737            .cyom(false)
738            .rfq_enabled(true)
739            .uma_resolution_status("resolved")
740            .game_id("game-1")
741            .sports_market_types(vec!["moneyline"])
742            .include_tag(true);
743    }
744}