1use polyoxide_core::QueryBuilder;
2use reqwest::Client;
3use serde::{Deserialize, Serialize};
4use url::Url;
5
6use crate::{
7 request::{AuthMode, Request},
8 types::OrderSide,
9};
10
11#[derive(Clone)]
13pub struct Markets {
14 pub(crate) client: Client,
15 pub(crate) base_url: Url,
16 pub(crate) chain_id: u64,
17}
18
19impl Markets {
20 pub fn get(&self, condition_id: impl Into<String>) -> Request<Market> {
22 Request::get(
23 self.client.clone(),
24 self.base_url.clone(),
25 format!("/markets/{}", urlencoding::encode(&condition_id.into())),
26 AuthMode::None,
27 self.chain_id,
28 )
29 }
30
31 pub fn get_by_token_ids(
32 &self,
33 token_ids: impl Into<Vec<String>>,
34 ) -> Request<ListMarketsResponse> {
35 Request::get(
36 self.client.clone(),
37 self.base_url.clone(),
38 "/markets",
39 AuthMode::None,
40 self.chain_id,
41 )
42 .query_many("clob_token_ids", token_ids.into())
43 }
44
45 pub fn list(&self) -> Request<ListMarketsResponse> {
47 Request::get(
48 self.client.clone(),
49 self.base_url.clone(),
50 "/markets",
51 AuthMode::None,
52 self.chain_id,
53 )
54 }
55
56 pub fn order_book(&self, token_id: impl Into<String>) -> Request<OrderBook> {
58 Request::get(
59 self.client.clone(),
60 self.base_url.clone(),
61 "/book",
62 AuthMode::None,
63 self.chain_id,
64 )
65 .query("token_id", token_id.into())
66 }
67
68 pub fn price(&self, token_id: impl Into<String>, side: OrderSide) -> Request<PriceResponse> {
70 Request::get(
71 self.client.clone(),
72 self.base_url.clone(),
73 "/price",
74 AuthMode::None,
75 self.chain_id,
76 )
77 .query("token_id", token_id.into())
78 .query("side", side.to_string())
79 }
80
81 pub fn midpoint(&self, token_id: impl Into<String>) -> Request<MidpointResponse> {
83 Request::get(
84 self.client.clone(),
85 self.base_url.clone(),
86 "/midpoint",
87 AuthMode::None,
88 self.chain_id,
89 )
90 .query("token_id", token_id.into())
91 }
92
93 pub fn prices_history(&self, token_id: impl Into<String>) -> Request<PricesHistoryResponse> {
95 Request::get(
96 self.client.clone(),
97 self.base_url.clone(),
98 "/prices-history",
99 AuthMode::None,
100 self.chain_id,
101 )
102 .query("market", token_id.into())
103 }
104
105 pub fn neg_risk(&self, token_id: impl Into<String>) -> Request<NegRiskResponse> {
107 Request::get(
108 self.client.clone(),
109 self.base_url.clone(),
110 "/neg-risk".to_string(),
111 AuthMode::None,
112 self.chain_id,
113 )
114 .query("token_id", token_id.into())
115 }
116
117 pub fn tick_size(&self, token_id: impl Into<String>) -> Request<TickSizeResponse> {
119 Request::get(
120 self.client.clone(),
121 self.base_url.clone(),
122 "/tick-size".to_string(),
123 AuthMode::None,
124 self.chain_id,
125 )
126 .query("token_id", token_id.into())
127 }
128}
129
130#[derive(Debug, Clone, Serialize, Deserialize)]
132pub struct Market {
133 pub condition_id: String,
134 pub question_id: String,
135 pub tokens: Vec<MarketToken>,
136 pub rewards: Option<serde_json::Value>,
137 pub minimum_order_size: f64,
138 pub minimum_tick_size: f64,
139 pub description: String,
140 pub category: Option<String>,
141 pub end_date_iso: Option<String>,
142 pub question: String,
143 pub active: bool,
144 pub closed: bool,
145 pub archived: bool,
146 pub neg_risk: Option<bool>,
147 pub neg_risk_market_id: Option<String>,
148 pub enable_order_book: Option<bool>,
149}
150
151#[derive(Debug, Clone, Serialize, Deserialize)]
153pub struct ListMarketsResponse {
154 pub data: Vec<Market>,
155 pub next_cursor: Option<String>,
156}
157
158#[derive(Debug, Clone, Serialize, Deserialize)]
160pub struct MarketToken {
161 pub token_id: Option<String>,
162 pub outcome: String,
163 pub price: Option<f64>,
164 pub winner: Option<bool>,
165}
166
167#[derive(Debug, Clone, Serialize, Deserialize)]
169pub struct OrderLevel {
170 pub price: String,
171 pub size: String,
172}
173
174#[derive(Debug, Clone, Serialize, Deserialize)]
176pub struct OrderBook {
177 pub market: String,
178 pub asset_id: String,
179 pub bids: Vec<OrderLevel>,
180 pub asks: Vec<OrderLevel>,
181 pub timestamp: String,
182 pub hash: String,
183}
184
185#[derive(Debug, Clone, Serialize, Deserialize)]
187pub struct PriceResponse {
188 pub price: String,
189}
190
191#[derive(Debug, Clone, Serialize, Deserialize)]
193pub struct MidpointResponse {
194 pub mid: String,
195}
196
197#[derive(Debug, Clone, Serialize, Deserialize)]
199pub struct PriceHistoryPoint {
200 #[serde(rename = "t")]
202 pub timestamp: i64,
203 #[serde(rename = "p")]
205 pub price: f64,
206}
207
208#[derive(Debug, Clone, Serialize, Deserialize)]
210pub struct PricesHistoryResponse {
211 pub history: Vec<PriceHistoryPoint>,
212}
213
214#[derive(Debug, Clone, Serialize, Deserialize)]
216pub struct NegRiskResponse {
217 pub neg_risk: bool,
218}
219
220#[derive(Debug, Clone, Serialize, Deserialize)]
222pub struct TickSizeResponse {
223 #[serde(deserialize_with = "deserialize_tick_size")]
224 pub minimum_tick_size: String,
225}
226
227fn deserialize_tick_size<'de, D>(deserializer: D) -> Result<String, D::Error>
228where
229 D: serde::Deserializer<'de>,
230{
231 use serde::Deserialize;
232 let v = serde_json::Value::deserialize(deserializer)?;
233 match v {
234 serde_json::Value::String(s) => Ok(s),
235 serde_json::Value::Number(n) => Ok(n.to_string()),
236 _ => Err(serde::de::Error::custom(
237 "expected string or number for tick size",
238 )),
239 }
240}