1use serde::Serialize;
2
3#[derive(Serialize, Debug)]
5pub struct Order {
6 r#type: String,
7 size: Option<f64>,
8 price: Option<f64>,
9 side: OrderSide,
10 client_oid: Option<String>,
11 self_trade_prevention: Option<SelfTradePrevention>,
12 time_in_force: Option<TimeInForce>,
13 cancel_after: Option<CancelAfter>,
14 post_only: Option<bool>,
15 funds: Option<f64>,
16 product_id: String,
17 stp: Option<String>,
18 stop: Option<OrderStop>,
19 stop_price: Option<f64>,
20}
21
22impl Order {
24 pub fn market_builder(
26 side: OrderSide,
27 product_id: &str,
28 size_or_funds: SizeOrFunds,
29 ) -> impl SharedOptions {
30 OrderBuilder {
31 r#type: "market".to_string(),
32 size: match size_or_funds {
33 SizeOrFunds::Size(n) => Some(n),
34 _ => None,
35 },
36 price: None,
37 side,
38 client_oid: None,
39 self_trade_prevention: None,
40 time_in_force: None,
41 cancel_after: None,
42 post_only: None,
43 funds: match size_or_funds {
44 SizeOrFunds::Funds(n) => Some(n),
45 _ => None,
46 },
47 product_id: product_id.to_string(),
48 stp: None,
49 stop: None,
50 stop_price: None,
51 }
52 }
53
54 pub fn limit_builder(
56 side: OrderSide,
57 product_id: &str,
58 price: f64,
59 size: f64,
60 ) -> impl LimitOptions + SharedOptions {
61 OrderBuilder {
62 r#type: "limit".to_string(),
63 size: Some(size),
64 price: Some(price),
65 side: side,
66 client_oid: None,
67 self_trade_prevention: None,
68 time_in_force: None,
69 cancel_after: None,
70 post_only: None,
71 funds: None,
72 product_id: product_id.to_string(),
73 stp: None,
74 stop: None,
75 stop_price: None,
76 }
77 }
78
79 pub fn stop_builder(
81 side: OrderSide,
82 product_id: &str,
83 price: f64,
84 size: f64,
85 stop_price: f64,
86 stop: OrderStop,
87 ) -> impl SharedOptions {
88 OrderBuilder {
89 r#type: "limit".to_string(),
90 size: Some(size),
91 price: Some(price),
92 side: side,
93 client_oid: None,
94 self_trade_prevention: None,
95 time_in_force: None,
96 cancel_after: None,
97 post_only: None,
98 funds: None,
99 product_id: product_id.to_string(),
100 stp: None,
101 stop: Some(stop),
102 stop_price: Some(stop_price),
103 }
104 }
105}
106
107pub struct OrderBuilder {
111 r#type: String,
112 size: Option<f64>,
113 price: Option<f64>,
114 side: OrderSide,
115 client_oid: Option<String>,
116 self_trade_prevention: Option<SelfTradePrevention>,
117 time_in_force: Option<TimeInForce>,
118 cancel_after: Option<CancelAfter>,
119 post_only: Option<bool>,
120 funds: Option<f64>,
121 product_id: String,
122 stp: Option<String>,
123 stop: Option<OrderStop>,
124 stop_price: Option<f64>,
125}
126
127impl OrderBuilder {
128 pub fn market(
130 side: OrderSide,
131 product_id: &str,
132 size_or_funds: SizeOrFunds,
133 ) -> impl SharedOptions {
134 Self {
135 r#type: "market".to_string(),
136 size: match size_or_funds {
137 SizeOrFunds::Size(n) => Some(n),
138 _ => None,
139 },
140 price: None,
141 side,
142 client_oid: None,
143 self_trade_prevention: None,
144 time_in_force: None,
145 cancel_after: None,
146 post_only: None,
147 funds: match size_or_funds {
148 SizeOrFunds::Funds(n) => Some(n),
149 _ => None,
150 },
151 product_id: product_id.to_string(),
152 stp: None,
153 stop: None,
154 stop_price: None,
155 }
156 }
157
158 pub fn limit(
160 side: OrderSide,
161 product_id: &str,
162 price: f64,
163 size: f64,
164 ) -> impl LimitOptions + SharedOptions {
165 Self {
166 r#type: "limit".to_string(),
167 size: Some(size),
168 price: Some(price),
169 side: side,
170 client_oid: None,
171 self_trade_prevention: None,
172 time_in_force: None,
173 cancel_after: None,
174 post_only: None,
175 funds: None,
176 product_id: product_id.to_string(),
177 stp: None,
178 stop: None,
179 stop_price: None,
180 }
181 }
182
183 pub fn stop(
185 side: OrderSide,
186 product_id: &str,
187 price: f64,
188 size: f64,
189 stop_price: f64,
190 stop: OrderStop,
191 ) -> impl SharedOptions {
192 Self {
193 r#type: "limit".to_string(),
194 size: Some(size),
195 price: Some(price),
196 side: side,
197 client_oid: None,
198 self_trade_prevention: None,
199 time_in_force: None,
200 cancel_after: None,
201 post_only: None,
202 funds: None,
203 product_id: product_id.to_string(),
204 stp: None,
205 stop: Some(stop),
206 stop_price: Some(stop_price),
207 }
208 }
209}
210
211pub trait SharedOptions {
213 fn self_trade_prevention(self, self_trade_prevention: SelfTradePrevention) -> Self;
214 fn client_oid(self, client_oid: String) -> Self;
215 fn build(self) -> Order;
216}
217
218impl SharedOptions for OrderBuilder {
219 fn self_trade_prevention(mut self, self_trade_prevention: SelfTradePrevention) -> Self {
221 self.self_trade_prevention = Some(self_trade_prevention);
222 self
223 }
224
225 fn client_oid(mut self, client_oid: String) -> Self {
234 self.client_oid = Some(client_oid);
235 self
236 }
237
238 fn build(self) -> Order {
240 Order {
241 r#type: self.r#type,
242 size: self.size,
243 price: self.price,
244 side: self.side,
245 client_oid: self.client_oid,
246 self_trade_prevention: self.self_trade_prevention,
247 time_in_force: self.time_in_force,
248 cancel_after: self.cancel_after,
249 post_only: self.post_only,
250 funds: self.funds,
251 product_id: self.product_id,
252 stp: self.stp,
253 stop: self.stop,
254 stop_price: self.stop_price,
255 }
256 }
257}
258
259pub trait LimitOptions {
261 fn time_in_force(self, time_in_force: TimeInForce) -> Self;
262}
263
264impl LimitOptions for OrderBuilder {
265 fn time_in_force(mut self, time_in_force: TimeInForce) -> Self {
267 match time_in_force {
268 TimeInForce::GoodTillTime {
269 cancel_after,
270 post_only,
271 } => {
272 self.cancel_after = Some(cancel_after);
273 self.post_only = Some(post_only);
274 }
275 TimeInForce::GoodTillCancel { post_only } => self.post_only = Some(post_only),
276 _ => {}
277 }
278 self.time_in_force = Some(time_in_force);
279 self
280 }
281}
282
283#[derive(Clone, Copy, Debug)]
285pub enum OrderSide {
286 Buy,
287 Sell,
288}
289#[derive(Clone, Copy, Debug)]
293pub enum OrderStop {
294 Loss,
295 Entry,
296}
297
298#[derive(Clone, Copy, Debug)]
300pub enum SizeOrFunds {
301 Size(f64),
302 Funds(f64),
303}
304
305#[derive(Clone, Copy, Debug)]
307pub enum TimeInForce {
308 GoodTillCancel {
309 post_only: bool,
310 },
311 GoodTillTime {
312 cancel_after: CancelAfter,
313 post_only: bool,
314 },
315 ImmediateOrCancel,
316 FillOrKill,
317}
318
319#[derive(Clone, Copy, Debug)]
320pub enum CancelAfter {
321 Minute,
322 Hour,
323 Day,
324}
325
326#[derive(Clone, Copy, Debug)]
328pub enum SelfTradePrevention {
329 DecreaseCancel,
330 CancelOldest,
331 CancelNewest,
332 CancelBoth,
333}
334
335impl serde::Serialize for SelfTradePrevention {
336 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
337 where
338 S: serde::ser::Serializer,
339 {
340 match self {
341 Self::CancelBoth => serializer.serialize_str("cb"),
342 Self::DecreaseCancel => serializer.serialize_str("dc"),
343 Self::CancelOldest => serializer.serialize_str("co"),
344 Self::CancelNewest => serializer.serialize_str("cn"),
345 }
346 }
347}
348
349impl serde::Serialize for OrderStop {
350 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
351 where
352 S: serde::ser::Serializer,
353 {
354 match self {
355 Self::Loss => serializer.serialize_str("loss"),
356 Self::Entry => serializer.serialize_str("entry"),
357 }
358 }
359}
360
361impl serde::Serialize for TimeInForce {
362 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
363 where
364 S: serde::ser::Serializer,
365 {
366 match self {
367 Self::GoodTillCancel { post_only: _ } => serializer.serialize_str("GTC"),
368 Self::GoodTillTime {
369 cancel_after: _,
370 post_only: _,
371 } => serializer.serialize_str("GTT"),
372 Self::ImmediateOrCancel => serializer.serialize_str("IOC"),
373 Self::FillOrKill => serializer.serialize_str("FOK"),
374 }
375 }
376}
377
378impl serde::Serialize for CancelAfter {
379 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
380 where
381 S: serde::ser::Serializer,
382 {
383 match self {
384 Self::Minute => serializer.serialize_str("min"),
385 Self::Hour => serializer.serialize_str("hour"),
386 Self::Day => serializer.serialize_str("day"),
387 }
388 }
389}
390
391impl serde::Serialize for OrderSide {
392 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
393 where
394 S: serde::ser::Serializer,
395 {
396 match self {
397 Self::Buy => serializer.serialize_str("buy"),
398 Self::Sell => serializer.serialize_str("sell"),
399 }
400 }
401}
402
403impl serde::Serialize for SizeOrFunds {
404 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
405 where
406 S: serde::ser::Serializer,
407 {
408 match *self {
409 Self::Size(size) => serializer.serialize_f64(size),
410 Self::Funds(funds) => serializer.serialize_f64(funds),
411 }
412 }
413}