1use pyo3::prelude::*;
17use serde_json;
18use ustr::Ustr;
19
20use crate::{
21 common::enums::{
22 BybitOrderSide, BybitOrderType, BybitProductType, BybitTimeInForce, BybitTriggerType,
23 },
24 websocket::{error::BybitWsError, messages},
25};
26
27#[pyclass]
29#[derive(Clone, Debug)]
30pub struct BybitWsPlaceOrderParams {
31 #[pyo3(get, set)]
32 pub category: BybitProductType,
33 #[pyo3(get, set)]
34 pub symbol: String,
35 #[pyo3(get, set)]
36 pub side: String,
37 #[pyo3(get, set)]
38 pub order_type: String,
39 #[pyo3(get, set)]
40 pub qty: String,
41 #[pyo3(get, set)]
42 pub is_leverage: Option<i32>,
43 #[pyo3(get, set)]
44 pub market_unit: Option<String>,
45 #[pyo3(get, set)]
46 pub price: Option<String>,
47 #[pyo3(get, set)]
48 pub time_in_force: Option<String>,
49 #[pyo3(get, set)]
50 pub order_link_id: Option<String>,
51 #[pyo3(get, set)]
52 pub reduce_only: Option<bool>,
53 #[pyo3(get, set)]
54 pub close_on_trigger: Option<bool>,
55 #[pyo3(get, set)]
56 pub trigger_price: Option<String>,
57 #[pyo3(get, set)]
58 pub trigger_by: Option<String>,
59 #[pyo3(get, set)]
60 pub trigger_direction: Option<i32>,
61 #[pyo3(get, set)]
62 pub tpsl_mode: Option<String>,
63 #[pyo3(get, set)]
64 pub take_profit: Option<String>,
65 #[pyo3(get, set)]
66 pub stop_loss: Option<String>,
67 #[pyo3(get, set)]
68 pub tp_trigger_by: Option<String>,
69 #[pyo3(get, set)]
70 pub sl_trigger_by: Option<String>,
71 #[pyo3(get, set)]
72 pub sl_trigger_price: Option<String>,
73 #[pyo3(get, set)]
74 pub tp_trigger_price: Option<String>,
75 #[pyo3(get, set)]
76 pub sl_order_type: Option<String>,
77 #[pyo3(get, set)]
78 pub tp_order_type: Option<String>,
79 #[pyo3(get, set)]
80 pub sl_limit_price: Option<String>,
81 #[pyo3(get, set)]
82 pub tp_limit_price: Option<String>,
83}
84
85#[pymethods]
86impl BybitWsPlaceOrderParams {
87 #[new]
88 #[allow(clippy::too_many_arguments)]
89 fn py_new(
90 category: BybitProductType,
91 symbol: String,
92 side: String,
93 order_type: String,
94 qty: String,
95 is_leverage: Option<i32>,
96 market_unit: Option<String>,
97 price: Option<String>,
98 time_in_force: Option<String>,
99 order_link_id: Option<String>,
100 reduce_only: Option<bool>,
101 close_on_trigger: Option<bool>,
102 trigger_price: Option<String>,
103 trigger_by: Option<String>,
104 trigger_direction: Option<i32>,
105 tpsl_mode: Option<String>,
106 take_profit: Option<String>,
107 stop_loss: Option<String>,
108 tp_trigger_by: Option<String>,
109 sl_trigger_by: Option<String>,
110 sl_trigger_price: Option<String>,
111 tp_trigger_price: Option<String>,
112 sl_order_type: Option<String>,
113 tp_order_type: Option<String>,
114 sl_limit_price: Option<String>,
115 tp_limit_price: Option<String>,
116 ) -> Self {
117 Self {
118 category,
119 symbol,
120 side,
121 order_type,
122 qty,
123 is_leverage,
124 market_unit,
125 price,
126 time_in_force,
127 order_link_id,
128 reduce_only,
129 close_on_trigger,
130 trigger_price,
131 trigger_by,
132 trigger_direction,
133 tpsl_mode,
134 take_profit,
135 stop_loss,
136 tp_trigger_by,
137 sl_trigger_by,
138 sl_trigger_price,
139 tp_trigger_price,
140 sl_order_type,
141 tp_order_type,
142 sl_limit_price,
143 tp_limit_price,
144 }
145 }
146}
147
148impl TryFrom<BybitWsPlaceOrderParams> for messages::BybitWsPlaceOrderParams {
149 type Error = BybitWsError;
150
151 fn try_from(params: BybitWsPlaceOrderParams) -> Result<Self, Self::Error> {
152 let side: BybitOrderSide =
153 serde_json::from_str(&format!("\"{}\"", params.side)).map_err(|e| {
154 BybitWsError::ClientError(format!("Invalid side '{}': {}", params.side, e))
155 })?;
156 let order_type: BybitOrderType =
157 serde_json::from_str(&format!("\"{}\"", params.order_type)).map_err(|e| {
158 BybitWsError::ClientError(format!(
159 "Invalid order_type '{}': {}",
160 params.order_type, e
161 ))
162 })?;
163
164 let time_in_force = params
165 .time_in_force
166 .map(|v| {
167 serde_json::from_str::<BybitTimeInForce>(&format!("\"{}\"", v)).map_err(|e| {
168 BybitWsError::ClientError(format!("Invalid time_in_force '{}': {}", v, e))
169 })
170 })
171 .transpose()?;
172
173 let trigger_by = params
174 .trigger_by
175 .map(|v| {
176 serde_json::from_str::<BybitTriggerType>(&format!("\"{}\"", v)).map_err(|e| {
177 BybitWsError::ClientError(format!("Invalid trigger_by '{}': {}", v, e))
178 })
179 })
180 .transpose()?;
181
182 let tp_trigger_by = params
183 .tp_trigger_by
184 .map(|v| {
185 serde_json::from_str::<BybitTriggerType>(&format!("\"{}\"", v)).map_err(|e| {
186 BybitWsError::ClientError(format!("Invalid tp_trigger_by '{}': {}", v, e))
187 })
188 })
189 .transpose()?;
190
191 let sl_trigger_by = params
192 .sl_trigger_by
193 .map(|v| {
194 serde_json::from_str::<BybitTriggerType>(&format!("\"{}\"", v)).map_err(|e| {
195 BybitWsError::ClientError(format!("Invalid sl_trigger_by '{}': {}", v, e))
196 })
197 })
198 .transpose()?;
199
200 let sl_order_type = params
201 .sl_order_type
202 .map(|v| {
203 serde_json::from_str::<BybitOrderType>(&format!("\"{}\"", v)).map_err(|e| {
204 BybitWsError::ClientError(format!("Invalid sl_order_type '{}': {}", v, e))
205 })
206 })
207 .transpose()?;
208
209 let tp_order_type = params
210 .tp_order_type
211 .map(|v| {
212 serde_json::from_str::<BybitOrderType>(&format!("\"{}\"", v)).map_err(|e| {
213 BybitWsError::ClientError(format!("Invalid tp_order_type '{}': {}", v, e))
214 })
215 })
216 .transpose()?;
217
218 Ok(Self {
219 category: params.category,
220 symbol: Ustr::from(¶ms.symbol),
221 side,
222 order_type,
223 qty: params.qty,
224 is_leverage: params.is_leverage,
225 market_unit: params.market_unit,
226 price: params.price,
227 time_in_force,
228 order_link_id: params.order_link_id,
229 reduce_only: params.reduce_only,
230 close_on_trigger: params.close_on_trigger,
231 trigger_price: params.trigger_price,
232 trigger_by,
233 trigger_direction: params.trigger_direction,
234 tpsl_mode: params.tpsl_mode,
235 take_profit: params.take_profit,
236 stop_loss: params.stop_loss,
237 tp_trigger_by,
238 sl_trigger_by,
239 sl_trigger_price: params.sl_trigger_price,
240 tp_trigger_price: params.tp_trigger_price,
241 sl_order_type,
242 tp_order_type,
243 sl_limit_price: params.sl_limit_price,
244 tp_limit_price: params.tp_limit_price,
245 })
246 }
247}
248
249impl From<messages::BybitWsPlaceOrderParams> for BybitWsPlaceOrderParams {
250 fn from(params: messages::BybitWsPlaceOrderParams) -> Self {
251 let side = serde_json::to_string(¶ms.side)
252 .expect("Failed to serialize BybitOrderSide")
253 .trim_matches('"')
254 .to_string();
255 let order_type = serde_json::to_string(¶ms.order_type)
256 .expect("Failed to serialize BybitOrderType")
257 .trim_matches('"')
258 .to_string();
259 let time_in_force = params.time_in_force.map(|v| {
260 serde_json::to_string(&v)
261 .expect("Failed to serialize BybitTimeInForce")
262 .trim_matches('"')
263 .to_string()
264 });
265 let trigger_by = params.trigger_by.map(|v| {
266 serde_json::to_string(&v)
267 .expect("Failed to serialize BybitTriggerType")
268 .trim_matches('"')
269 .to_string()
270 });
271 let tp_trigger_by = params.tp_trigger_by.map(|v| {
272 serde_json::to_string(&v)
273 .expect("Failed to serialize BybitTriggerType")
274 .trim_matches('"')
275 .to_string()
276 });
277 let sl_trigger_by = params.sl_trigger_by.map(|v| {
278 serde_json::to_string(&v)
279 .expect("Failed to serialize BybitTriggerType")
280 .trim_matches('"')
281 .to_string()
282 });
283 let sl_order_type = params.sl_order_type.map(|v| {
284 serde_json::to_string(&v)
285 .expect("Failed to serialize BybitOrderType")
286 .trim_matches('"')
287 .to_string()
288 });
289 let tp_order_type = params.tp_order_type.map(|v| {
290 serde_json::to_string(&v)
291 .expect("Failed to serialize BybitOrderType")
292 .trim_matches('"')
293 .to_string()
294 });
295
296 Self {
297 category: params.category,
298 symbol: params.symbol.to_string(),
299 side,
300 order_type,
301 qty: params.qty,
302 is_leverage: params.is_leverage,
303 market_unit: params.market_unit,
304 price: params.price,
305 time_in_force,
306 order_link_id: params.order_link_id,
307 reduce_only: params.reduce_only,
308 close_on_trigger: params.close_on_trigger,
309 trigger_price: params.trigger_price,
310 trigger_by,
311 trigger_direction: params.trigger_direction,
312 tpsl_mode: params.tpsl_mode,
313 take_profit: params.take_profit,
314 stop_loss: params.stop_loss,
315 tp_trigger_by,
316 sl_trigger_by,
317 sl_trigger_price: params.sl_trigger_price,
318 tp_trigger_price: params.tp_trigger_price,
319 sl_order_type,
320 tp_order_type,
321 sl_limit_price: params.sl_limit_price,
322 tp_limit_price: params.tp_limit_price,
323 }
324 }
325}
326
327#[pyclass]
329#[derive(Clone, Debug)]
330pub struct BybitWsAmendOrderParams {
331 #[pyo3(get, set)]
332 pub category: BybitProductType,
333 #[pyo3(get, set)]
334 pub symbol: String,
335 #[pyo3(get, set)]
336 pub order_id: Option<String>,
337 #[pyo3(get, set)]
338 pub order_link_id: Option<String>,
339 #[pyo3(get, set)]
340 pub qty: Option<String>,
341 #[pyo3(get, set)]
342 pub price: Option<String>,
343 #[pyo3(get, set)]
344 pub trigger_price: Option<String>,
345 #[pyo3(get, set)]
346 pub take_profit: Option<String>,
347 #[pyo3(get, set)]
348 pub stop_loss: Option<String>,
349 #[pyo3(get, set)]
350 pub tp_trigger_by: Option<String>,
351 #[pyo3(get, set)]
352 pub sl_trigger_by: Option<String>,
353}
354
355#[pymethods]
356impl BybitWsAmendOrderParams {
357 #[new]
358 #[allow(clippy::too_many_arguments)]
359 fn py_new(
360 category: BybitProductType,
361 symbol: String,
362 order_id: Option<String>,
363 order_link_id: Option<String>,
364 qty: Option<String>,
365 price: Option<String>,
366 trigger_price: Option<String>,
367 take_profit: Option<String>,
368 stop_loss: Option<String>,
369 tp_trigger_by: Option<String>,
370 sl_trigger_by: Option<String>,
371 ) -> Self {
372 Self {
373 category,
374 symbol,
375 order_id,
376 order_link_id,
377 qty,
378 price,
379 trigger_price,
380 take_profit,
381 stop_loss,
382 tp_trigger_by,
383 sl_trigger_by,
384 }
385 }
386}
387
388impl TryFrom<BybitWsAmendOrderParams> for messages::BybitWsAmendOrderParams {
389 type Error = BybitWsError;
390
391 fn try_from(params: BybitWsAmendOrderParams) -> Result<Self, Self::Error> {
392 let tp_trigger_by = params
393 .tp_trigger_by
394 .map(|v| {
395 serde_json::from_str::<BybitTriggerType>(&format!("\"{}\"", v)).map_err(|e| {
396 BybitWsError::ClientError(format!("Invalid tp_trigger_by '{}': {}", v, e))
397 })
398 })
399 .transpose()?;
400
401 let sl_trigger_by = params
402 .sl_trigger_by
403 .map(|v| {
404 serde_json::from_str::<BybitTriggerType>(&format!("\"{}\"", v)).map_err(|e| {
405 BybitWsError::ClientError(format!("Invalid sl_trigger_by '{}': {}", v, e))
406 })
407 })
408 .transpose()?;
409
410 Ok(Self {
411 category: params.category,
412 symbol: Ustr::from(¶ms.symbol),
413 order_id: params.order_id,
414 order_link_id: params.order_link_id,
415 qty: params.qty,
416 price: params.price,
417 trigger_price: params.trigger_price,
418 take_profit: params.take_profit,
419 stop_loss: params.stop_loss,
420 tp_trigger_by,
421 sl_trigger_by,
422 })
423 }
424}
425
426impl From<messages::BybitWsAmendOrderParams> for BybitWsAmendOrderParams {
427 fn from(params: messages::BybitWsAmendOrderParams) -> Self {
428 let tp_trigger_by = params.tp_trigger_by.map(|v| {
429 serde_json::to_string(&v)
430 .expect("Failed to serialize BybitTriggerType")
431 .trim_matches('"')
432 .to_string()
433 });
434 let sl_trigger_by = params.sl_trigger_by.map(|v| {
435 serde_json::to_string(&v)
436 .expect("Failed to serialize BybitTriggerType")
437 .trim_matches('"')
438 .to_string()
439 });
440
441 Self {
442 category: params.category,
443 symbol: params.symbol.to_string(),
444 order_id: params.order_id,
445 order_link_id: params.order_link_id,
446 qty: params.qty,
447 price: params.price,
448 trigger_price: params.trigger_price,
449 take_profit: params.take_profit,
450 stop_loss: params.stop_loss,
451 tp_trigger_by,
452 sl_trigger_by,
453 }
454 }
455}