1use crate::client::RestClient;
7use crate::error::{Error, Result};
8use crate::models::{CreateOrderRequest, CreateOrderResponse, OrderConfiguration, OrderSide, StopDirection};
9
10pub struct MarketOrderBuilder<'a> {
12 client: &'a RestClient,
13 product_id: Option<String>,
14 side: Option<OrderSide>,
15 quote_size: Option<String>,
16 base_size: Option<String>,
17 client_order_id: Option<String>,
18}
19
20impl<'a> MarketOrderBuilder<'a> {
21 pub(crate) fn new(client: &'a RestClient) -> Self {
23 Self {
24 client,
25 product_id: None,
26 side: None,
27 quote_size: None,
28 base_size: None,
29 client_order_id: None,
30 }
31 }
32
33 pub fn product_id(mut self, product_id: impl Into<String>) -> Self {
35 self.product_id = Some(product_id.into());
36 self
37 }
38
39 pub fn buy(mut self, product_id: impl Into<String>) -> Self {
41 self.product_id = Some(product_id.into());
42 self.side = Some(OrderSide::Buy);
43 self
44 }
45
46 pub fn sell(mut self, product_id: impl Into<String>) -> Self {
48 self.product_id = Some(product_id.into());
49 self.side = Some(OrderSide::Sell);
50 self
51 }
52
53 pub fn quote_size(mut self, quote_size: impl Into<String>) -> Self {
55 self.quote_size = Some(quote_size.into());
56 self
57 }
58
59 pub fn base_size(mut self, base_size: impl Into<String>) -> Self {
61 self.base_size = Some(base_size.into());
62 self
63 }
64
65 pub fn client_order_id(mut self, client_order_id: impl Into<String>) -> Self {
67 self.client_order_id = Some(client_order_id.into());
68 self
69 }
70
71 pub async fn send(self) -> Result<CreateOrderResponse> {
73 let product_id = self.product_id
74 .ok_or_else(|| Error::request("product_id is required"))?;
75 let side = self.side
76 .ok_or_else(|| Error::request("side is required (use .buy() or .sell())"))?;
77
78 let config = if let Some(quote_size) = self.quote_size {
79 OrderConfiguration::market_buy_quote(quote_size)
80 } else if let Some(base_size) = self.base_size {
81 if side == OrderSide::Buy {
82 OrderConfiguration::market_buy_base(base_size)
83 } else {
84 OrderConfiguration::market_sell(base_size)
85 }
86 } else {
87 return Err(Error::request("either quote_size or base_size is required"));
88 };
89
90 let client_order_id = self.client_order_id
91 .unwrap_or_else(uuid_v4);
92
93 let request = CreateOrderRequest::new(client_order_id, product_id, side, config);
94 self.client.orders().create(request).await
95 }
96}
97
98pub struct LimitOrderGtcBuilder<'a> {
100 client: &'a RestClient,
101 product_id: Option<String>,
102 side: Option<OrderSide>,
103 base_size: Option<String>,
104 limit_price: Option<String>,
105 post_only: bool,
106 client_order_id: Option<String>,
107}
108
109impl<'a> LimitOrderGtcBuilder<'a> {
110 pub(crate) fn new(client: &'a RestClient) -> Self {
112 Self {
113 client,
114 product_id: None,
115 side: None,
116 base_size: None,
117 limit_price: None,
118 post_only: false,
119 client_order_id: None,
120 }
121 }
122
123 pub fn buy(mut self, product_id: impl Into<String>) -> Self {
125 self.product_id = Some(product_id.into());
126 self.side = Some(OrderSide::Buy);
127 self
128 }
129
130 pub fn sell(mut self, product_id: impl Into<String>) -> Self {
132 self.product_id = Some(product_id.into());
133 self.side = Some(OrderSide::Sell);
134 self
135 }
136
137 pub fn base_size(mut self, base_size: impl Into<String>) -> Self {
139 self.base_size = Some(base_size.into());
140 self
141 }
142
143 pub fn limit_price(mut self, limit_price: impl Into<String>) -> Self {
145 self.limit_price = Some(limit_price.into());
146 self
147 }
148
149 pub fn post_only(mut self, post_only: bool) -> Self {
151 self.post_only = post_only;
152 self
153 }
154
155 pub fn client_order_id(mut self, client_order_id: impl Into<String>) -> Self {
157 self.client_order_id = Some(client_order_id.into());
158 self
159 }
160
161 pub async fn send(self) -> Result<CreateOrderResponse> {
163 let product_id = self.product_id
164 .ok_or_else(|| Error::request("product_id is required"))?;
165 let side = self.side
166 .ok_or_else(|| Error::request("side is required (use .buy() or .sell())"))?;
167 let base_size = self.base_size
168 .ok_or_else(|| Error::request("base_size is required"))?;
169 let limit_price = self.limit_price
170 .ok_or_else(|| Error::request("limit_price is required"))?;
171
172 let config = OrderConfiguration::limit_gtc(base_size, limit_price, self.post_only);
173 let client_order_id = self.client_order_id.unwrap_or_else(uuid_v4);
174
175 let request = CreateOrderRequest::new(client_order_id, product_id, side, config);
176 self.client.orders().create(request).await
177 }
178}
179
180pub struct LimitOrderGtdBuilder<'a> {
182 client: &'a RestClient,
183 product_id: Option<String>,
184 side: Option<OrderSide>,
185 base_size: Option<String>,
186 limit_price: Option<String>,
187 end_time: Option<String>,
188 post_only: bool,
189 client_order_id: Option<String>,
190}
191
192impl<'a> LimitOrderGtdBuilder<'a> {
193 pub(crate) fn new(client: &'a RestClient) -> Self {
195 Self {
196 client,
197 product_id: None,
198 side: None,
199 base_size: None,
200 limit_price: None,
201 end_time: None,
202 post_only: false,
203 client_order_id: None,
204 }
205 }
206
207 pub fn buy(mut self, product_id: impl Into<String>) -> Self {
209 self.product_id = Some(product_id.into());
210 self.side = Some(OrderSide::Buy);
211 self
212 }
213
214 pub fn sell(mut self, product_id: impl Into<String>) -> Self {
216 self.product_id = Some(product_id.into());
217 self.side = Some(OrderSide::Sell);
218 self
219 }
220
221 pub fn base_size(mut self, base_size: impl Into<String>) -> Self {
223 self.base_size = Some(base_size.into());
224 self
225 }
226
227 pub fn limit_price(mut self, limit_price: impl Into<String>) -> Self {
229 self.limit_price = Some(limit_price.into());
230 self
231 }
232
233 pub fn end_time(mut self, end_time: impl Into<String>) -> Self {
235 self.end_time = Some(end_time.into());
236 self
237 }
238
239 pub fn post_only(mut self, post_only: bool) -> Self {
241 self.post_only = post_only;
242 self
243 }
244
245 pub fn client_order_id(mut self, client_order_id: impl Into<String>) -> Self {
247 self.client_order_id = Some(client_order_id.into());
248 self
249 }
250
251 pub async fn send(self) -> Result<CreateOrderResponse> {
253 let product_id = self.product_id
254 .ok_or_else(|| Error::request("product_id is required"))?;
255 let side = self.side
256 .ok_or_else(|| Error::request("side is required (use .buy() or .sell())"))?;
257 let base_size = self.base_size
258 .ok_or_else(|| Error::request("base_size is required"))?;
259 let limit_price = self.limit_price
260 .ok_or_else(|| Error::request("limit_price is required"))?;
261 let end_time = self.end_time
262 .ok_or_else(|| Error::request("end_time is required"))?;
263
264 let config = OrderConfiguration::limit_gtd(base_size, limit_price, end_time, self.post_only);
265 let client_order_id = self.client_order_id.unwrap_or_else(uuid_v4);
266
267 let request = CreateOrderRequest::new(client_order_id, product_id, side, config);
268 self.client.orders().create(request).await
269 }
270}
271
272pub struct StopLimitOrderGtcBuilder<'a> {
274 client: &'a RestClient,
275 product_id: Option<String>,
276 side: Option<OrderSide>,
277 base_size: Option<String>,
278 limit_price: Option<String>,
279 stop_price: Option<String>,
280 stop_direction: Option<StopDirection>,
281 client_order_id: Option<String>,
282}
283
284impl<'a> StopLimitOrderGtcBuilder<'a> {
285 pub(crate) fn new(client: &'a RestClient) -> Self {
287 Self {
288 client,
289 product_id: None,
290 side: None,
291 base_size: None,
292 limit_price: None,
293 stop_price: None,
294 stop_direction: None,
295 client_order_id: None,
296 }
297 }
298
299 pub fn buy(mut self, product_id: impl Into<String>) -> Self {
301 self.product_id = Some(product_id.into());
302 self.side = Some(OrderSide::Buy);
303 self
304 }
305
306 pub fn sell(mut self, product_id: impl Into<String>) -> Self {
308 self.product_id = Some(product_id.into());
309 self.side = Some(OrderSide::Sell);
310 self
311 }
312
313 pub fn base_size(mut self, base_size: impl Into<String>) -> Self {
315 self.base_size = Some(base_size.into());
316 self
317 }
318
319 pub fn limit_price(mut self, limit_price: impl Into<String>) -> Self {
321 self.limit_price = Some(limit_price.into());
322 self
323 }
324
325 pub fn stop_price(mut self, stop_price: impl Into<String>) -> Self {
327 self.stop_price = Some(stop_price.into());
328 self
329 }
330
331 pub fn stop_direction(mut self, stop_direction: StopDirection) -> Self {
333 self.stop_direction = Some(stop_direction);
334 self
335 }
336
337 pub fn client_order_id(mut self, client_order_id: impl Into<String>) -> Self {
339 self.client_order_id = Some(client_order_id.into());
340 self
341 }
342
343 pub async fn send(self) -> Result<CreateOrderResponse> {
345 let product_id = self.product_id
346 .ok_or_else(|| Error::request("product_id is required"))?;
347 let side = self.side
348 .ok_or_else(|| Error::request("side is required (use .buy() or .sell())"))?;
349 let base_size = self.base_size
350 .ok_or_else(|| Error::request("base_size is required"))?;
351 let limit_price = self.limit_price
352 .ok_or_else(|| Error::request("limit_price is required"))?;
353 let stop_price = self.stop_price
354 .ok_or_else(|| Error::request("stop_price is required"))?;
355 let stop_direction = self.stop_direction
356 .ok_or_else(|| Error::request("stop_direction is required"))?;
357
358 let config = OrderConfiguration::stop_limit_gtc(base_size, limit_price, stop_price, stop_direction);
359 let client_order_id = self.client_order_id.unwrap_or_else(uuid_v4);
360
361 let request = CreateOrderRequest::new(client_order_id, product_id, side, config);
362 self.client.orders().create(request).await
363 }
364}
365
366fn uuid_v4() -> String {
368 use std::time::{SystemTime, UNIX_EPOCH};
369
370 let now = SystemTime::now()
371 .duration_since(UNIX_EPOCH)
372 .unwrap_or_default()
373 .as_nanos();
374
375 format!(
377 "{:08x}-{:04x}-4{:03x}-{:04x}-{:012x}",
378 (now >> 96) as u32,
379 (now >> 80) as u16,
380 (now >> 68) as u16 & 0x0fff,
381 ((now >> 52) as u16 & 0x3fff) | 0x8000,
382 now as u64 & 0xffffffffffff
383 )
384}
385
386impl RestClient {
388 pub fn market_order(&self) -> MarketOrderBuilder<'_> {
416 MarketOrderBuilder::new(self)
417 }
418
419 pub fn limit_order_gtc(&self) -> LimitOrderGtcBuilder<'_> {
441 LimitOrderGtcBuilder::new(self)
442 }
443
444 pub fn limit_order_gtd(&self) -> LimitOrderGtdBuilder<'_> {
466 LimitOrderGtdBuilder::new(self)
467 }
468
469 pub fn stop_limit_order_gtc(&self) -> StopLimitOrderGtcBuilder<'_> {
492 StopLimitOrderGtcBuilder::new(self)
493 }
494}