1use chrono::{DateTime, Utc};
19use derive_builder::Builder;
20use serde::{self, Deserialize, Serialize, Serializer};
21use serde_json::Value;
22
23fn serialize_json_as_string<S>(value: &Option<Value>, serializer: S) -> Result<S::Ok, S::Error>
25where
26 S: Serializer,
27{
28 match value {
29 Some(v) => serializer.serialize_str(&v.to_string()),
30 None => serializer.serialize_none(),
31 }
32}
33
34use crate::common::enums::{
35 BitmexContingencyType, BitmexExecInstruction, BitmexOrderType, BitmexPegPriceType, BitmexSide,
36 BitmexTimeInForce,
37};
38
39fn serialize_string_vec_as_json<S>(
40 values: &Option<Vec<String>>,
41 serializer: S,
42) -> Result<S::Ok, S::Error>
43where
44 S: serde::Serializer,
45{
46 match values {
47 Some(vec) => {
48 let json_array = serde_json::to_string(vec).map_err(serde::ser::Error::custom)?;
49 serializer.serialize_str(&json_array)
50 }
51 None => serializer.serialize_none(),
52 }
53}
54
55#[derive(Clone, Debug, Deserialize, Serialize, Default, Builder)]
57#[builder(default)]
58#[builder(setter(into, strip_option))]
59#[serde(rename_all = "camelCase")]
60pub struct GetTradeParams {
61 pub symbol: Option<String>,
63 #[serde(
65 skip_serializing_if = "Option::is_none",
66 serialize_with = "serialize_json_as_string"
67 )]
68 pub filter: Option<Value>,
69 #[serde(
71 skip_serializing_if = "Option::is_none",
72 serialize_with = "serialize_json_as_string"
73 )]
74 pub columns: Option<Value>,
75 #[serde(skip_serializing_if = "Option::is_none")]
77 pub count: Option<i32>,
78 #[serde(skip_serializing_if = "Option::is_none")]
80 pub start: Option<i32>,
81 #[serde(skip_serializing_if = "Option::is_none")]
83 pub reverse: Option<bool>,
84 #[serde(skip_serializing_if = "Option::is_none")]
86 pub start_time: Option<DateTime<Utc>>,
87 #[serde(skip_serializing_if = "Option::is_none")]
89 pub end_time: Option<DateTime<Utc>>,
90}
91
92#[derive(Clone, Debug, Deserialize, Serialize, Default, Builder)]
94#[builder(default)]
95#[builder(setter(into, strip_option))]
96#[serde(rename_all = "camelCase")]
97pub struct GetTradeBucketedParams {
98 #[serde(skip_serializing_if = "Option::is_none")]
100 pub symbol: Option<String>,
101 #[serde(skip_serializing_if = "Option::is_none")]
103 pub bin_size: Option<String>,
104 #[serde(skip_serializing_if = "Option::is_none")]
106 pub partial: Option<bool>,
107 #[serde(
109 skip_serializing_if = "Option::is_none",
110 serialize_with = "serialize_json_as_string"
111 )]
112 pub filter: Option<Value>,
113 #[serde(
115 skip_serializing_if = "Option::is_none",
116 serialize_with = "serialize_json_as_string"
117 )]
118 pub columns: Option<Value>,
119 #[serde(skip_serializing_if = "Option::is_none")]
121 pub count: Option<i32>,
122 #[serde(skip_serializing_if = "Option::is_none")]
124 pub start: Option<i32>,
125 #[serde(skip_serializing_if = "Option::is_none")]
127 pub reverse: Option<bool>,
128 #[serde(skip_serializing_if = "Option::is_none")]
130 pub start_time: Option<DateTime<Utc>>,
131 #[serde(skip_serializing_if = "Option::is_none")]
133 pub end_time: Option<DateTime<Utc>>,
134}
135
136#[derive(Clone, Debug, Deserialize, Serialize, Default, Builder)]
138#[builder(default)]
139#[builder(setter(into, strip_option))]
140#[serde(rename_all = "camelCase")]
141pub struct GetOrderParams {
142 #[serde(skip_serializing_if = "Option::is_none")]
144 pub symbol: Option<String>,
145 #[serde(
147 skip_serializing_if = "Option::is_none",
148 serialize_with = "serialize_json_as_string"
149 )]
150 pub filter: Option<Value>,
151 #[serde(
153 skip_serializing_if = "Option::is_none",
154 serialize_with = "serialize_json_as_string"
155 )]
156 pub columns: Option<Value>,
157 #[serde(skip_serializing_if = "Option::is_none")]
159 pub count: Option<i32>,
160 #[serde(skip_serializing_if = "Option::is_none")]
162 pub start: Option<i32>,
163 #[serde(skip_serializing_if = "Option::is_none")]
165 pub reverse: Option<bool>,
166 #[serde(skip_serializing_if = "Option::is_none")]
168 pub start_time: Option<DateTime<Utc>>,
169 #[serde(skip_serializing_if = "Option::is_none")]
171 pub end_time: Option<DateTime<Utc>>,
172}
173
174#[derive(Clone, Debug, Deserialize, Serialize, Default, Builder)]
176#[builder(default)]
177#[builder(setter(into, strip_option))]
178#[serde(rename_all = "camelCase")]
179pub struct PostOrderParams {
180 pub symbol: String,
182 #[serde(skip_serializing_if = "Option::is_none")]
184 pub side: Option<BitmexSide>,
185 #[serde(skip_serializing_if = "Option::is_none")]
187 pub order_qty: Option<u32>,
188 #[serde(skip_serializing_if = "Option::is_none")]
190 pub price: Option<f64>,
191 #[serde(skip_serializing_if = "Option::is_none")]
193 pub display_qty: Option<u32>,
194 #[serde(skip_serializing_if = "Option::is_none")]
196 pub stop_px: Option<f64>,
197 #[serde(skip_serializing_if = "Option::is_none")]
199 #[serde(rename = "clOrdID")]
200 pub cl_ord_id: Option<String>,
201 #[serde(skip_serializing_if = "Option::is_none")]
203 #[serde(rename = "clOrdLinkID")]
204 pub cl_ord_link_id: Option<String>,
205 #[serde(skip_serializing_if = "Option::is_none")]
207 pub peg_offset_value: Option<f64>,
208 #[serde(skip_serializing_if = "Option::is_none")]
210 pub peg_price_type: Option<BitmexPegPriceType>,
211 #[serde(skip_serializing_if = "Option::is_none")]
213 pub ord_type: Option<BitmexOrderType>,
214 #[serde(skip_serializing_if = "Option::is_none")]
216 pub time_in_force: Option<BitmexTimeInForce>,
217 #[serde(
219 serialize_with = "serialize_exec_instructions_optional",
220 skip_serializing_if = "is_exec_inst_empty"
221 )]
222 pub exec_inst: Option<Vec<BitmexExecInstruction>>,
223 #[serde(skip_serializing_if = "Option::is_none")]
225 pub contingency_type: Option<BitmexContingencyType>,
226 #[serde(skip_serializing_if = "Option::is_none")]
228 pub text: Option<String>,
229}
230
231fn is_exec_inst_empty(exec_inst: &Option<Vec<BitmexExecInstruction>>) -> bool {
232 exec_inst.as_ref().is_none_or(Vec::is_empty)
233}
234
235fn serialize_exec_instructions_optional<S>(
236 instructions: &Option<Vec<BitmexExecInstruction>>,
237 serializer: S,
238) -> Result<S::Ok, S::Error>
239where
240 S: serde::Serializer,
241{
242 match instructions {
243 Some(inst) if !inst.is_empty() => {
244 let joined = inst
245 .iter()
246 .map(std::string::ToString::to_string)
247 .collect::<Vec<_>>()
248 .join(",");
249 serializer.serialize_some(&joined)
250 }
251 _ => serializer.serialize_none(),
252 }
253}
254
255#[derive(Clone, Debug, Deserialize, Serialize, Default, Builder)]
257#[builder(default)]
258#[builder(setter(into, strip_option))]
259#[serde(rename_all = "camelCase")]
260pub struct DeleteOrderParams {
261 #[serde(
263 skip_serializing_if = "Option::is_none",
264 serialize_with = "serialize_string_vec_as_json",
265 rename = "orderID"
266 )]
267 pub order_id: Option<Vec<String>>,
268 #[serde(
270 skip_serializing_if = "Option::is_none",
271 serialize_with = "serialize_string_vec_as_json",
272 rename = "clOrdID"
273 )]
274 pub cl_ord_id: Option<Vec<String>>,
275 #[serde(skip_serializing_if = "Option::is_none")]
276 pub text: Option<String>,
278}
279
280impl DeleteOrderParamsBuilder {
281 pub fn build_validated(self) -> Result<DeleteOrderParams, String> {
287 let params = self.build().map_err(|e| format!("Failed to build: {e}"))?;
288
289 if params.order_id.is_some() && params.cl_ord_id.is_some() {
291 return Err("Cannot provide both order_id and cl_ord_id - use only one".to_string());
292 }
293
294 if params.order_id.is_none() && params.cl_ord_id.is_none() {
296 return Err("Must provide either order_id or cl_ord_id".to_string());
297 }
298
299 Ok(params)
300 }
301}
302
303#[derive(Clone, Debug, Deserialize, Serialize, Default, Builder)]
309#[builder(default)]
310#[builder(setter(into, strip_option))]
311#[serde(rename_all = "camelCase")]
312pub struct DeleteAllOrdersParams {
313 #[serde(skip_serializing_if = "Option::is_none")]
315 pub symbol: Option<String>,
316 #[serde(
318 skip_serializing_if = "Option::is_none",
319 serialize_with = "serialize_json_as_string"
320 )]
321 pub filter: Option<Value>,
322 #[serde(skip_serializing_if = "Option::is_none")]
324 pub text: Option<String>,
325}
326
327#[derive(Clone, Debug, Deserialize, Serialize, Default, Builder)]
329#[builder(default)]
330#[builder(setter(into, strip_option))]
331#[serde(rename_all = "camelCase")]
332pub struct PutOrderParams {
333 #[serde(rename = "orderID")]
335 pub order_id: Option<String>,
336 #[serde(rename = "origClOrdID")]
338 pub orig_cl_ord_id: Option<String>,
339 #[serde(rename = "clOrdID")]
341 pub cl_ord_id: Option<String>,
342 pub order_qty: Option<u32>,
344 pub leaves_qty: Option<u32>,
346 pub price: Option<f64>,
348 pub stop_px: Option<f64>,
350 pub peg_offset_value: Option<f64>,
352 pub text: Option<String>,
354}
355
356#[derive(Clone, Debug, Deserialize, Serialize, Default, Builder)]
358#[builder(default)]
359#[builder(setter(into, strip_option))]
360#[serde(rename_all = "camelCase")]
361pub struct GetExecutionParams {
362 #[serde(skip_serializing_if = "Option::is_none")]
364 pub symbol: Option<String>,
365 #[serde(
367 skip_serializing_if = "Option::is_none",
368 serialize_with = "serialize_json_as_string"
369 )]
370 pub filter: Option<Value>,
371 #[serde(
373 skip_serializing_if = "Option::is_none",
374 serialize_with = "serialize_json_as_string"
375 )]
376 pub columns: Option<Value>,
377 #[serde(skip_serializing_if = "Option::is_none")]
379 pub count: Option<i32>,
380 #[serde(skip_serializing_if = "Option::is_none")]
382 pub start: Option<i32>,
383 #[serde(skip_serializing_if = "Option::is_none")]
385 pub reverse: Option<bool>,
386 #[serde(skip_serializing_if = "Option::is_none")]
388 pub start_time: Option<DateTime<Utc>>,
389 #[serde(skip_serializing_if = "Option::is_none")]
391 pub end_time: Option<DateTime<Utc>>,
392}
393
394#[derive(Clone, Debug, Deserialize, Serialize, Default, Builder)]
396#[builder(default)]
397#[builder(setter(into, strip_option))]
398#[serde(rename_all = "camelCase")]
399pub struct PostPositionLeverageParams {
400 pub symbol: String,
402 pub leverage: f64,
404 #[serde(skip_serializing_if = "Option::is_none")]
406 pub target_account_id: Option<i64>,
407}
408
409#[derive(Clone, Debug, Deserialize, Serialize, Default, Builder)]
411#[builder(default)]
412#[builder(setter(into, strip_option))]
413#[serde(rename_all = "camelCase")]
414pub struct GetPositionParams {
415 #[serde(
417 skip_serializing_if = "Option::is_none",
418 serialize_with = "serialize_json_as_string"
419 )]
420 pub filter: Option<Value>,
421 #[serde(
423 skip_serializing_if = "Option::is_none",
424 serialize_with = "serialize_json_as_string"
425 )]
426 pub columns: Option<Value>,
427 #[serde(skip_serializing_if = "Option::is_none")]
429 pub count: Option<i32>,
430}