bybit/http/orders.rs
1use rust_decimal::{Decimal, serde::str_option::deserialize as option_decimal};
2use serde::{Deserialize, Serialize};
3use serde_aux::prelude::deserialize_number_from_string as number;
4
5use super::common::List;
6use crate::{
7 CancelType, CreateType, OcoTriggerBy, OrderStatus, OrderType, PlaceType, PositionIdx,
8 RejectReason, Side, SmpType, TimeInForce, Timestamp, TpslMode, TriggerBy, TriggerDirection,
9 enums::{Category, StopOrderType},
10 serde::{empty_string_as_none, string_to_option_bool},
11 ws::OrderMsg,
12};
13
14/// Query parameters of `GET /v5/order/realtime` ([`Client::get_open_closed_orders`](crate::http::Client::get_open_closed_orders)).
15#[derive(Clone, Debug, Serialize)]
16#[serde(rename_all = "camelCase")]
17pub struct GetOpenClosedOrdersParams {
18 /// Product type
19 /// UTA2.0, UTA1.0: linear, inverse, spot, option
20 /// classic account: linear, inverse, spot
21 pub category: Category,
22 /// Symbol name, like BTCUSDT, uppercase only. For linear, either symbol, baseCoin, settleCoin is required
23 #[serde(skip_serializing_if = "Option::is_none")]
24 pub symbol: Option<String>,
25 /// Base coin, uppercase only
26 /// Supports linear, inverse & option
27 /// option: it returns all option open orders by default
28 #[serde(skip_serializing_if = "Option::is_none")]
29 pub base_coin: Option<String>,
30 /// Settle coin, uppercase only
31 /// linear: either symbol, baseCoin or settleCoin is required
32 /// spot: not supported
33 /// option: USDT or USDC
34 #[serde(skip_serializing_if = "Option::is_none")]
35 pub settle_coin: Option<String>,
36 /// Order ID
37 #[serde(skip_serializing_if = "Option::is_none")]
38 pub order_id: Option<String>,
39 /// User customized order ID
40 #[serde(skip_serializing_if = "Option::is_none")]
41 pub order_link_id: Option<String>,
42 /// 0(default): UTA2.0, UTA1.0, classic account query open status orders (e.g., New, PartiallyFilled) only
43 /// 1: UTA2.0, UTA1.0(except inverse)
44 /// 2: UTA1.0(inverse), classic account
45 /// Query a maximum of recent 500 closed status records are kept under each account each category (e.g., Cancelled, Rejected, Filled orders).
46 /// If the Bybit service is restarted due to an update, this part of the data will be cleared and accumulated again, but the order records will still be queried in order history
47 /// openOnly param will be ignored when query by orderId or orderLinkId
48 /// Classic spot: not supported
49 #[serde(skip_serializing_if = "Option::is_none")]
50 pub open_only: Option<i32>,
51 /// Order: active order,
52 /// StopOrder: conditional order for Futures and Spot,
53 /// tpslOrder: spot TP/SL order,
54 /// OcoOrder: Spot oco order,
55 /// BidirectionalTpslOrder: Spot bidirectional TPSL order
56 /// - classic account spot: return Order active order by default
57 /// - Others: all kinds of orders by default
58 #[serde(skip_serializing_if = "Option::is_none")]
59 pub order_filter: Option<OrderFilter>,
60 /// Limit for data size per page. [1, 50]. Default: 20
61 #[serde(skip_serializing_if = "Option::is_none")]
62 pub limit: Option<i32>,
63 /// Cursor. Use the nextPageCursor token from the response to retrieve the next page of the result set
64 #[serde(skip_serializing_if = "Option::is_none")]
65 pub cursor: Option<String>,
66}
67
68impl GetOpenClosedOrdersParams {
69 /// Create with the required fields; set the optional ones with the `with_*` methods.
70 pub fn new(category: Category) -> Self {
71 Self {
72 category,
73 symbol: None,
74 base_coin: None,
75 settle_coin: None,
76 order_id: None,
77 order_link_id: None,
78 open_only: None,
79 order_filter: None,
80 limit: None,
81 cursor: None,
82 }
83 }
84
85 /// Set `symbol`: symbol name, like BTCUSDT, uppercase only. For linear, either symbol, baseCoin, settleCoin is required.
86 pub fn with_symbol(mut self, v: impl Into<String>) -> Self {
87 self.symbol = Some(v.into());
88 self
89 }
90 /// Set `base_coin`: base coin, uppercase only Supports linear, inverse & option option: it returns all option open orders by default.
91 pub fn with_base_coin(mut self, v: impl Into<String>) -> Self {
92 self.base_coin = Some(v.into());
93 self
94 }
95 /// Set `settle_coin`: settle coin, uppercase only linear: either symbol, baseCoin or settleCoin is required spot: not supported option: USDT or USDC.
96 pub fn with_settle_coin(mut self, v: impl Into<String>) -> Self {
97 self.settle_coin = Some(v.into());
98 self
99 }
100 /// Set `order_id`: order ID.
101 pub fn with_order_id(mut self, v: impl Into<String>) -> Self {
102 self.order_id = Some(v.into());
103 self
104 }
105 /// Set `order_link_id`: user customized order ID.
106 pub fn with_order_link_id(mut self, v: impl Into<String>) -> Self {
107 self.order_link_id = Some(v.into());
108 self
109 }
110 /// Set `open_only`: 0(default): UTA2.0, UTA1.0, classic account query open status orders (e.g., New, PartiallyFilled) only 1: UTA2.0, UTA1.0(except inverse) 2: UTA1.0(inverse), classic account Query a maximum of recent 500 closed status records are kept under ...
111 pub fn with_open_only(mut self, v: i32) -> Self {
112 self.open_only = Some(v);
113 self
114 }
115 /// Set `order_filter`: order: active order, StopOrder: conditional order for Futures and Spot, tpslOrder: spot TP/SL order, OcoOrder: Spot oco order, BidirectionalTpslOrder: Spot bidirectional TPSL order - classic account spot: return Order active order by ...
116 pub fn with_order_filter(mut self, v: OrderFilter) -> Self {
117 self.order_filter = Some(v);
118 self
119 }
120 /// Set `limit`: limit for data size per page. [1, 50]. Default: 20.
121 pub fn with_limit(mut self, v: i32) -> Self {
122 self.limit = Some(v);
123 self
124 }
125 /// Set `cursor`: cursor. Use the nextPageCursor token from the response to retrieve the next page of the result set.
126 pub fn with_cursor(mut self, v: impl Into<String>) -> Self {
127 self.cursor = Some(v.into());
128 self
129 }
130}
131
132/// Part of the request of `/v5/order/cancel`.
133#[derive(Debug, Clone, Serialize)]
134pub enum OrderFilter {
135 /// active order,
136 Order,
137 /// conditional order for Futures and Spot,
138 StopOrder,
139 /// spot TP/SL order,
140 #[serde(rename = "camelCase")]
141 TpslOrder,
142 /// Spot oco order,
143 OcoOrder,
144 /// Spot bidirectional TPSL order
145 /// - classic account spot: return Order active order by default
146 /// - Others: all kinds of orders by default
147 BidirectionalTpslOrder,
148}
149
150/// Item of the list returned by `GET /v5/order/realtime` ([`Client::get_open_closed_orders`](crate::http::Client::get_open_closed_orders), [`Client::get_order_history`](crate::http::Client::get_order_history)).
151#[derive(Debug, Deserialize, PartialEq, Clone)]
152#[serde(rename_all = "camelCase")]
153pub struct Order {
154 /// Order ID
155 pub order_id: String,
156 /// User customized order ID
157 #[serde(default, deserialize_with = "empty_string_as_none")]
158 pub order_link_id: Option<String>,
159 /// Paradigm block trade ID
160 #[serde(default, deserialize_with = "empty_string_as_none")]
161 pub block_trade_id: Option<String>,
162 /// Symbol name
163 pub symbol: String,
164 /// Order price
165 pub price: Decimal,
166 /// Order qty
167 pub qty: Decimal,
168 /// Side. Buy,Sell
169 pub side: Side,
170 /// Whether to borrow. Unified spot only. 0: false, 1: true. Classic spot is not supported, always 0
171 #[serde(default, deserialize_with = "string_to_option_bool")]
172 pub is_leverage: Option<bool>,
173 /// Position index. Used to identify positions in different position modes.
174 pub position_idx: PositionIdx,
175 /// Order status
176 pub order_status: OrderStatus,
177 /// Order create type
178 /// Only for category=linear or inverse
179 /// Spot, Option do not have this key
180 #[serde(default, deserialize_with = "empty_string_as_none")]
181 pub create_type: Option<CreateType>,
182 /// Cancel type
183 pub cancel_type: CancelType,
184 /// Reject reason. Classic spot is not supported
185 pub reject_reason: RejectReason,
186 /// Average filled price
187 /// UTA: returns "" for those orders without avg price
188 /// classic account: returns "0" for those orders without avg price, and also for those orders have partially filled but cancelled at the end
189 #[serde(default, deserialize_with = "option_decimal")]
190 pub avg_price: Option<Decimal>,
191 /// The remaining qty not executed. Classic spot is not supported
192 pub leaves_qty: Decimal,
193 /// The estimated value not executed. Classic spot is not supported
194 pub leaves_value: Decimal,
195 /// Cumulative executed order qty
196 pub cum_exec_qty: Decimal,
197 /// Cumulative executed order value. Classic spot is not supported
198 pub cum_exec_value: Decimal,
199 /// Cumulative executed trading fee. Classic spot is not supported
200 pub cum_exec_fee: Decimal,
201 /// Time in force
202 pub time_in_force: TimeInForce,
203 /// Order type. Market,Limit. For TP/SL order, it means the order type after triggered
204 pub order_type: OrderType,
205 /// Stop order type
206 #[serde(default, deserialize_with = "empty_string_as_none")]
207 pub stop_order_type: Option<StopOrderType>,
208 /// Implied volatility
209 #[serde(default, deserialize_with = "option_decimal")]
210 pub order_iv: Option<Decimal>,
211 /// The unit for qty when create Spot market orders for UTA account. baseCoin, quoteCoin
212 #[serde(default, deserialize_with = "empty_string_as_none")]
213 pub market_unit: Option<String>,
214 /// Trigger price. If stopOrderType=TrailingStop, it is activate price. Otherwise, it is trigger price
215 #[serde(default, deserialize_with = "option_decimal")]
216 pub trigger_price: Option<Decimal>,
217 /// Take profit price
218 #[serde(default, deserialize_with = "option_decimal")]
219 pub take_profit: Option<Decimal>,
220 /// Stop loss price
221 #[serde(default, deserialize_with = "option_decimal")]
222 pub stop_loss: Option<Decimal>,
223 /// TP/SL mode, Full: entire position for TP/SL. Partial: partial position tp/sl. Spot does not have this field, and Option returns always ""
224 #[serde(default, deserialize_with = "empty_string_as_none")]
225 pub tpsl_mode: Option<TpslMode>,
226 /// The trigger type of Spot OCO order.OcoTriggerByUnknown, OcoTriggerByTp, OcoTriggerByBySl. Classic spot is not supported
227 #[serde(default, deserialize_with = "empty_string_as_none")]
228 pub oco_trigger_by: Option<OcoTriggerBy>,
229 /// The limit order price when take profit price is triggered
230 #[serde(default, deserialize_with = "option_decimal")]
231 pub tp_limit_price: Option<Decimal>,
232 /// The limit order price when stop loss price is triggered
233 #[serde(default, deserialize_with = "option_decimal")]
234 pub sl_limit_price: Option<Decimal>,
235 /// The price type to trigger take profit
236 #[serde(default, deserialize_with = "empty_string_as_none")]
237 pub tp_trigger_by: Option<TriggerBy>,
238 /// The price type to trigger stop loss
239 #[serde(default, deserialize_with = "empty_string_as_none")]
240 pub sl_trigger_by: Option<TriggerBy>,
241 /// Trigger direction. 1: rise, 2: fall
242 pub trigger_direction: TriggerDirection,
243 /// The price type of trigger price
244 #[serde(default, deserialize_with = "empty_string_as_none")]
245 pub trigger_by: Option<TriggerBy>,
246 /// Last price when place the order, Spot is not applicable
247 #[serde(default, deserialize_with = "option_decimal")]
248 pub last_price_on_created: Option<Decimal>,
249 /// Last price when place the order, Spot has this field only
250 #[serde(default, deserialize_with = "option_decimal")]
251 pub base_price: Option<Decimal>,
252 /// Reduce only. true means reduce position size
253 pub reduce_only: bool,
254 /// Close on trigger. What is a close on trigger order?
255 pub close_on_trigger: bool,
256 /// Place type, option used. iv, price
257 #[serde(default, deserialize_with = "empty_string_as_none")]
258 pub place_type: Option<PlaceType>,
259 /// SMP execution type
260 pub smp_type: SmpType,
261 /// Smp group ID. If the UID has no group, it is 0 by default
262 pub smp_group: i64,
263 /// The counterparty's orderID which triggers this SMP execution
264 #[serde(default, deserialize_with = "empty_string_as_none")]
265 pub smp_order_id: Option<String>,
266 /// Order created timestamp (ms)
267 #[serde(deserialize_with = "number")]
268 pub created_time: Timestamp,
269 /// Order updated timestamp (ms)
270 #[serde(deserialize_with = "number")]
271 pub updated_time: Timestamp,
272}
273
274impl Order {
275 /// The order is still active (see [`OrderStatus::is_open`](crate::OrderStatus::is_open)).
276 pub fn is_open_status(&self) -> bool {
277 self.order_status.is_open()
278 }
279 /// The order reached a final status.
280 pub fn is_closed_status(&self) -> bool {
281 self.order_status.is_closed()
282 }
283 /// Apply an `order` stream update; `leavesQty`/`leavesValue` keep their value when the update omits them.
284 pub fn update(&mut self, msg: OrderMsg) {
285 self.order_id = msg.order_id;
286 self.order_link_id = msg.order_link_id;
287 self.block_trade_id = msg.block_trade_id;
288 self.symbol = msg.symbol;
289 self.price = msg.price;
290 self.qty = msg.qty;
291 self.side = msg.side;
292 self.is_leverage = msg.is_leverage;
293 self.position_idx = msg.position_idx;
294 self.order_status = msg.order_status;
295 self.create_type = msg.create_type;
296 self.cancel_type = msg.cancel_type;
297 self.reject_reason = msg.reject_reason;
298 self.avg_price = msg.avg_price;
299 if let Some(leaves_qty) = msg.leaves_qty {
300 self.leaves_qty = leaves_qty;
301 }
302 if let Some(leaves_value) = msg.leaves_value {
303 self.leaves_value = leaves_value;
304 }
305 self.cum_exec_qty = msg.cum_exec_qty;
306 self.cum_exec_value = msg.cum_exec_value;
307 self.cum_exec_fee = msg.cum_exec_fee;
308 self.time_in_force = msg.time_in_force;
309 self.order_type = msg.order_type;
310 self.stop_order_type = msg.stop_order_type;
311 self.order_iv = msg.order_iv;
312 self.market_unit = msg.market_unit;
313 self.trigger_price = msg.trigger_price;
314 self.take_profit = msg.take_profit;
315 self.stop_loss = msg.stop_loss;
316 self.tpsl_mode = msg.tpsl_mode;
317 self.oco_trigger_by = msg.oco_trigger_by;
318 self.tp_limit_price = msg.tp_limit_price;
319 self.sl_limit_price = msg.sl_limit_price;
320 self.tp_trigger_by = msg.tp_trigger_by;
321 self.sl_trigger_by = msg.sl_trigger_by;
322 self.trigger_direction = msg.trigger_direction;
323 self.trigger_by = msg.trigger_by;
324 self.last_price_on_created = msg.last_price_on_created;
325 // `base_price` is not sent by the order stream: keep the snapshot value.
326 self.reduce_only = msg.reduce_only;
327 self.close_on_trigger = msg.close_on_trigger;
328 self.place_type = msg.place_type;
329 self.smp_type = msg.smp_type;
330 self.smp_group = msg.smp_group;
331 self.smp_order_id = msg.smp_order_id;
332 self.created_time = msg.created_time;
333 self.updated_time = msg.updated_time;
334 }
335}
336
337/// Build an [`Order`] from its first `order` stream message (an order that
338/// was not in a REST snapshot).
339///
340/// `base_price` is not sent by the stream and stays `None`. When the stream
341/// omits `leavesQty`/`leavesValue`, they are derived as `qty - cumExecQty`
342/// and `price * leavesQty`.
343impl From<OrderMsg> for Order {
344 fn from(msg: OrderMsg) -> Self {
345 let leaves_qty = msg.leaves_qty.unwrap_or(msg.qty - msg.cum_exec_qty);
346 let leaves_value = msg.leaves_value.unwrap_or(msg.price * leaves_qty);
347 Self {
348 order_id: msg.order_id,
349 order_link_id: msg.order_link_id,
350 block_trade_id: msg.block_trade_id,
351 symbol: msg.symbol,
352 price: msg.price,
353 qty: msg.qty,
354 side: msg.side,
355 is_leverage: msg.is_leverage,
356 position_idx: msg.position_idx,
357 order_status: msg.order_status,
358 create_type: msg.create_type,
359 cancel_type: msg.cancel_type,
360 reject_reason: msg.reject_reason,
361 avg_price: msg.avg_price,
362 leaves_qty,
363 leaves_value,
364 cum_exec_qty: msg.cum_exec_qty,
365 cum_exec_value: msg.cum_exec_value,
366 cum_exec_fee: msg.cum_exec_fee,
367 time_in_force: msg.time_in_force,
368 order_type: msg.order_type,
369 stop_order_type: msg.stop_order_type,
370 order_iv: msg.order_iv,
371 market_unit: msg.market_unit,
372 trigger_price: msg.trigger_price,
373 take_profit: msg.take_profit,
374 stop_loss: msg.stop_loss,
375 tpsl_mode: msg.tpsl_mode,
376 oco_trigger_by: msg.oco_trigger_by,
377 tp_limit_price: msg.tp_limit_price,
378 sl_limit_price: msg.sl_limit_price,
379 tp_trigger_by: msg.tp_trigger_by,
380 sl_trigger_by: msg.sl_trigger_by,
381 trigger_direction: msg.trigger_direction,
382 trigger_by: msg.trigger_by,
383 last_price_on_created: msg.last_price_on_created,
384 base_price: None,
385 reduce_only: msg.reduce_only,
386 close_on_trigger: msg.close_on_trigger,
387 place_type: msg.place_type,
388 smp_type: msg.smp_type,
389 smp_group: msg.smp_group,
390 smp_order_id: msg.smp_order_id,
391 created_time: msg.created_time,
392 updated_time: msg.updated_time,
393 }
394 }
395}
396
397/// Request body of `POST /v5/order/create` ([`Client::place_order`](crate::http::Client::place_order)).
398#[derive(Debug, Serialize, Clone)]
399#[serde(rename_all = "camelCase")]
400pub struct PlaceOrderRequest {
401 /// Product type
402 /// UTA2.0, UTA1.0: linear, inverse, spot, option
403 /// classic account: linear, inverse, spot
404 pub category: Category,
405 /// Symbol name, like BTCUSDT, uppercase only
406 pub symbol: String,
407 // Whether to borrow. Unified account Spot trading only.
408 /// 0(default): false, spot trading
409 /// 1: true, margin trading, make sure you turn on margin trading, and set the relevant currency as collateral
410 #[serde(skip_serializing_if = "Option::is_none")]
411 pub is_leverage: Option<i64>,
412 /// Buy, Sell
413 pub side: Side,
414 /// Market, Limit
415 pub order_type: OrderType,
416 /// Order quantity
417 /// UTA account
418 /// Spot: Market Buy order by value by default, you can set marketUnit field to choose order by value or qty for market orders
419 /// Perps, Futures & Option: always order by qty
420 /// classic account
421 /// Spot: Market Buy order by value by default
422 /// Perps, Futures: always order by qty
423 /// Perps & Futures: if you pass qty="0" and specify reduceOnly=true&closeOnTrigger=true, you can close the position up to maxMktOrderQty or maxOrderQty shown on Get Instruments Info of current symbol
424 pub qty: Decimal,
425 /// Select the unit for qty when create Spot market orders for UTA account
426 /// baseCoin: for example, buy BTCUSDT, then "qty" unit is BTC
427 /// quoteCoin: for example, sell BTCUSDT, then "qty" unit is USDT
428 #[serde(skip_serializing_if = "Option::is_none")]
429 pub market_unit: Option<String>,
430 /// Slippage tolerance Type for market order, TickSize, Percent
431 /// take profit, stoploss, conditional orders are not supported
432 /// TickSize:
433 /// the highest price of Buy order = ask1 + slippageTolerance x tickSize;
434 /// the lowest price of Sell order = bid1 - slippageTolerance x tickSize
435 /// Percent:
436 /// the highest price of Buy order = ask1 x (1 + slippageTolerance x 0.01);
437 /// the lowest price of Sell order = bid1 x (1 - slippageTolerance x 0.01)
438 #[serde(skip_serializing_if = "Option::is_none")]
439 pub slippage_tolerance_type: Option<Decimal>,
440 /// Slippage tolerance value
441 /// TickSize: range is [1, 10000], integer only
442 /// Percent: range is [0.01, 10], up to 2 decimals
443 #[serde(skip_serializing_if = "Option::is_none")]
444 pub slippage_tolerance: Option<Decimal>,
445 /// Order price
446 /// Market order will ignore this field
447 /// Please check the min price and price precision from instrument info endpoint
448 /// If you have position, price needs to be better than liquidation price
449 #[serde(skip_serializing_if = "Option::is_none")]
450 pub price: Option<Decimal>,
451 /// Conditional order param. Used to identify the expected direction of the conditional order.
452 /// 1: triggered when market price rises to triggerPrice
453 /// 2: triggered when market price falls to triggerPrice
454 /// Valid for linear & inverse
455 #[serde(skip_serializing_if = "Option::is_none")]
456 pub trigger_direction: Option<TriggerDirection>,
457 /// If it is not passed, Order by default.
458 /// Order
459 /// tpslOrder: Spot TP/SL order, the assets are occupied even before the order is triggered
460 /// StopOrder: Spot conditional order, the assets will not be occupied until the price of the underlying asset reaches the trigger price, and the required assets will be occupied after the Conditional order is triggered
461 /// Valid for spot only
462 #[serde(skip_serializing_if = "Option::is_none")]
463 pub order_filter: Option<String>,
464 /// For Perps & Futures, it is the conditional order trigger price. If you expect the price to rise to trigger your conditional order, make sure:
465 /// triggerPrice > market price
466 /// Else, triggerPrice < market price
467 /// For spot, it is the TP/SL and Conditional order trigger price
468 #[serde(skip_serializing_if = "Option::is_none")]
469 pub trigger_price: Option<Decimal>,
470 /// Trigger price type, Conditional order param for Perps & Futures.
471 /// LastPrice
472 /// IndexPrice
473 /// MarkPrice
474 /// Valid for linear & inverse
475 #[serde(skip_serializing_if = "Option::is_none")]
476 pub trigger_by: Option<TriggerBy>,
477 /// Implied volatility. option only. Pass the real value, e.g for 10%, 0.1 should be passed. orderIv has a higher priority when price is passed as well
478 #[serde(skip_serializing_if = "Option::is_none")]
479 pub order_iv: Option<Decimal>,
480 /// Time in force
481 /// Market order will always use IOC
482 /// If not passed, GTC is used by default
483 #[serde(skip_serializing_if = "Option::is_none")]
484 pub time_in_force: Option<TimeInForce>,
485 /// Used to identify positions in different position modes. Under hedge-mode, this param is required
486 /// 0: one-way mode
487 /// 1: hedge-mode Buy side
488 /// 2: hedge-mode Sell side
489 #[serde(skip_serializing_if = "Option::is_none")]
490 pub position_idx: Option<PositionIdx>,
491 /// User customised order ID. A max of 36 characters. Combinations of numbers, letters (upper and lower cases), dashes, and underscores are supported.
492 /// Futures & Perps: orderLinkId rules:
493 /// optional param
494 /// always unique
495 /// option orderLinkId rules:
496 /// required param
497 /// always unique
498 #[serde(skip_serializing_if = "Option::is_none")]
499 pub order_link_id: Option<String>,
500 /// Take profit price
501 /// UTA: Spot Limit order supports take profit, stop loss or limit take profit, limit stop loss when creating an order
502 #[serde(skip_serializing_if = "Option::is_none")]
503 pub take_profit: Option<Decimal>,
504 /// Stop loss price
505 /// UTA: Spot Limit order supports take profit, stop loss or limit take profit, limit stop loss when creating an order
506 #[serde(skip_serializing_if = "Option::is_none")]
507 pub stop_loss: Option<Decimal>,
508 /// The price type to trigger take profit. MarkPrice, IndexPrice, default: LastPrice. Valid for linear & inverse
509 #[serde(skip_serializing_if = "Option::is_none")]
510 pub tp_trigger_by: Option<TriggerBy>,
511 /// The price type to trigger stop loss. MarkPrice, IndexPrice, default: LastPrice. Valid for linear & inverse
512 #[serde(skip_serializing_if = "Option::is_none")]
513 pub sl_trigger_by: Option<TriggerBy>,
514 /// What is a reduce-only order? true means your position can only reduce in size if this order is triggered.
515 /// You must specify it as true when you are about to close/reduce the position
516 /// When reduceOnly is true, take profit/stop loss cannot be set
517 /// Valid for linear, inverse & option
518 #[serde(skip_serializing_if = "Option::is_none")]
519 pub reduce_only: Option<bool>,
520 /// What is a close on trigger order? For a closing order. It can only reduce your position, not increase it. If the account has insufficient available balance when the closing order is triggered, then other active orders of similar contracts will be cancelled or reduced. It can be used to ensure your stop loss reduces your position regardless of current available margin.
521 /// Valid for linear & inverse
522 #[serde(skip_serializing_if = "Option::is_none")]
523 pub close_on_trigger: Option<bool>,
524 /// Smp execution type. What is SMP?
525 #[serde(skip_serializing_if = "Option::is_none")]
526 pub smp_type: Option<SmpType>,
527 /// Market maker protection. option only. true means set the order as a market maker protection order. What is mmp?
528 #[serde(skip_serializing_if = "Option::is_none")]
529 pub mmp: Option<bool>,
530 /// TP/SL mode
531 /// Full: entire position for TP/SL. Then, tpOrderType or slOrderType must be Market
532 /// Partial: partial position tp/sl (as there is no size option, so it will create tp/sl orders with the qty you actually fill). Limit TP/SL order are supported. Note: When create limit tp/sl, tpslMode is required and it must be Partial
533 /// Valid for linear & inverse
534 #[serde(skip_serializing_if = "Option::is_none")]
535 pub tpsl_mode: Option<TpslMode>,
536 /// The limit order price when take profit price is triggered
537 /// linear & inverse: only works when tpslMode=Partial and tpOrderType=Limit
538 /// Spot(UTA): it is required when the order has takeProfit and "tpOrderType"=Limit
539 #[serde(skip_serializing_if = "Option::is_none")]
540 pub tp_limit_price: Option<Decimal>,
541 /// The limit order price when stop loss price is triggered
542 /// linear & inverse: only works when tpslMode=Partial and slOrderType=Limit
543 /// Spot(UTA): it is required when the order has stopLoss and "slOrderType"=Limit
544 #[serde(skip_serializing_if = "Option::is_none")]
545 pub sl_limit_price: Option<Decimal>,
546 /// The order type when take profit is triggered
547 /// linear & inverse: Market(default), Limit. For tpslMode=Full, it only supports tpOrderType=Market
548 /// Spot(UTA):
549 /// Market: when you set "takeProfit",
550 /// Limit: when you set "takeProfit" and "tpLimitPrice"
551 #[serde(skip_serializing_if = "Option::is_none")]
552 pub tp_order_type: Option<OrderType>,
553 /// The order type when stop loss is triggered
554 /// linear & inverse: Market(default), Limit. For tpslMode=Full, it only supports slOrderType=Market
555 /// Spot(UTA):
556 /// Market: when you set "stopLoss",
557 /// Limit: when you set "stopLoss" and "slLimitPrice"
558 #[serde(skip_serializing_if = "Option::is_none")]
559 pub sl_order_type: Option<OrderType>,
560 /// Queue: use the order price on the orderbook in the same direction as the side
561 /// Counterparty: use the order price on the orderbook in the opposite direction as the side
562 /// Valid for linear & inverse
563 #[serde(skip_serializing_if = "Option::is_none")]
564 pub bbo_side_type: Option<String>,
565 /// 1,2,3,4,5 Valid for linear & inverse
566 #[serde(skip_serializing_if = "Option::is_none")]
567 pub bbo_level: Option<String>,
568}
569
570impl PlaceOrderRequest {
571 /// Create with the required fields; set the optional ones with the `with_*` methods.
572 pub fn new(
573 category: Category,
574 symbol: impl Into<String>,
575 side: Side,
576 order_type: OrderType,
577 qty: Decimal,
578 ) -> Self {
579 let symbol: String = symbol.into();
580 Self {
581 category,
582 symbol,
583 is_leverage: None,
584 side,
585 order_type,
586 qty,
587 market_unit: None,
588 slippage_tolerance_type: None,
589 slippage_tolerance: None,
590 price: None,
591 trigger_direction: None,
592 order_filter: None,
593 trigger_price: None,
594 trigger_by: None,
595 order_iv: None,
596 time_in_force: None,
597 position_idx: None,
598 order_link_id: None,
599 take_profit: None,
600 stop_loss: None,
601 tp_trigger_by: None,
602 sl_trigger_by: None,
603 reduce_only: None,
604 close_on_trigger: None,
605 smp_type: None,
606 mmp: None,
607 tpsl_mode: None,
608 tp_limit_price: None,
609 sl_limit_price: None,
610 tp_order_type: None,
611 sl_order_type: None,
612 bbo_side_type: None,
613 bbo_level: None,
614 }
615 }
616
617 /// Set `is_leverage`: 0(default): false, spot trading 1: true, margin trading, make sure you turn on margin trading, and set the relevant currency as collateral.
618 pub fn with_is_leverage(mut self, v: i64) -> Self {
619 self.is_leverage = Some(v);
620 self
621 }
622 /// Set `market_unit`: select the unit for qty when create Spot market orders for UTA account baseCoin: for example, buy BTCUSDT, then "qty" unit is BTC quoteCoin: for example, sell BTCUSDT, then "qty" unit is USDT.
623 pub fn with_market_unit(mut self, v: impl Into<String>) -> Self {
624 self.market_unit = Some(v.into());
625 self
626 }
627 /// Set `slippage_tolerance_type`: slippage tolerance Type for market order, TickSize, Percent take profit, stoploss, conditional orders are not supported TickSize: the highest price of Buy order = ask1 + slippageTolerance x tickSize;.
628 pub fn with_slippage_tolerance_type(mut self, v: Decimal) -> Self {
629 self.slippage_tolerance_type = Some(v);
630 self
631 }
632 /// Set `slippage_tolerance`: slippage tolerance value TickSize: range is [1, 10000], integer only Percent: range is [0.01, 10], up to 2 decimals.
633 pub fn with_slippage_tolerance(mut self, v: Decimal) -> Self {
634 self.slippage_tolerance = Some(v);
635 self
636 }
637 /// Set `price`: order price Market order will ignore this field Please check the min price and price precision from instrument info endpoint If you have position, price needs to be better than liquidation price.
638 pub fn with_price(mut self, v: Decimal) -> Self {
639 self.price = Some(v);
640 self
641 }
642 /// Set `trigger_direction`: conditional order param. Used to identify the expected direction of the conditional order. 1: triggered when market price rises to triggerPrice 2: triggered when market price falls to triggerPrice Valid for linear & inverse.
643 pub fn with_trigger_direction(mut self, v: TriggerDirection) -> Self {
644 self.trigger_direction = Some(v);
645 self
646 }
647 /// Set `order_filter`: if it is not passed, Order by default. Order tpslOrder: Spot TP/SL order, the assets are occupied even before the order is triggered StopOrder: Spot conditional order, the assets will not be occupied until the price of the underlying asset ...
648 pub fn with_order_filter(mut self, v: impl Into<String>) -> Self {
649 self.order_filter = Some(v.into());
650 self
651 }
652 /// Set `trigger_price`: for Perps & Futures, it is the conditional order trigger price.
653 pub fn with_trigger_price(mut self, v: Decimal) -> Self {
654 self.trigger_price = Some(v);
655 self
656 }
657 /// Set `trigger_by`: trigger price type, Conditional order param for Perps & Futures. LastPrice IndexPrice MarkPrice Valid for linear & inverse.
658 pub fn with_trigger_by(mut self, v: TriggerBy) -> Self {
659 self.trigger_by = Some(v);
660 self
661 }
662 /// Set `order_iv`: implied volatility. option only. Pass the real value, e.g for 10%, 0.1 should be passed. orderIv has a higher priority when price is passed as well.
663 pub fn with_order_iv(mut self, v: Decimal) -> Self {
664 self.order_iv = Some(v);
665 self
666 }
667 /// Set `time_in_force`: time in force Market order will always use IOC If not passed, GTC is used by default.
668 pub fn with_time_in_force(mut self, v: TimeInForce) -> Self {
669 self.time_in_force = Some(v);
670 self
671 }
672 /// Set `position_idx`: used to identify positions in different position modes. Under hedge-mode, this param is required 0: one-way mode 1: hedge-mode Buy side 2: hedge-mode Sell side.
673 pub fn with_position_idx(mut self, v: PositionIdx) -> Self {
674 self.position_idx = Some(v);
675 self
676 }
677 /// Set `order_link_id`: user customised order ID. A max of 36 characters. Combinations of numbers, letters (upper and lower cases), dashes, and underscores are supported.
678 pub fn with_order_link_id(mut self, v: impl Into<String>) -> Self {
679 self.order_link_id = Some(v.into());
680 self
681 }
682 /// Set `take_profit`: take profit price UTA: Spot Limit order supports take profit, stop loss or limit take profit, limit stop loss when creating an order.
683 pub fn with_take_profit(mut self, v: Decimal) -> Self {
684 self.take_profit = Some(v);
685 self
686 }
687 /// Set `stop_loss`: stop loss price UTA: Spot Limit order supports take profit, stop loss or limit take profit, limit stop loss when creating an order.
688 pub fn with_stop_loss(mut self, v: Decimal) -> Self {
689 self.stop_loss = Some(v);
690 self
691 }
692 /// Set `tp_trigger_by`: the price type to trigger take profit. MarkPrice, IndexPrice, default: LastPrice. Valid for linear & inverse.
693 pub fn with_tp_trigger_by(mut self, v: TriggerBy) -> Self {
694 self.tp_trigger_by = Some(v);
695 self
696 }
697 /// Set `sl_trigger_by`: the price type to trigger stop loss. MarkPrice, IndexPrice, default: LastPrice. Valid for linear & inverse.
698 pub fn with_sl_trigger_by(mut self, v: TriggerBy) -> Self {
699 self.sl_trigger_by = Some(v);
700 self
701 }
702 /// Set `reduce_only`: what is a reduce-only order? true means your position can only reduce in size if this order is triggered.
703 pub fn with_reduce_only(mut self, v: bool) -> Self {
704 self.reduce_only = Some(v);
705 self
706 }
707 /// Set `close_on_trigger`: what is a close on trigger order? For a closing order. It can only reduce your position, not increase it.
708 pub fn with_close_on_trigger(mut self, v: bool) -> Self {
709 self.close_on_trigger = Some(v);
710 self
711 }
712 /// Set `smp_type`: smp execution type. What is SMP?
713 pub fn with_smp_type(mut self, v: SmpType) -> Self {
714 self.smp_type = Some(v);
715 self
716 }
717 /// Set `mmp`: market maker protection. option only. true means set the order as a market maker protection order. What is mmp?
718 pub fn with_mmp(mut self, v: bool) -> Self {
719 self.mmp = Some(v);
720 self
721 }
722 /// Set `tpsl_mode`: tP/SL mode Full: entire position for TP/SL. Then, tpOrderType or slOrderType must be Market Partial: partial position tp/sl (as there is no size option, so it will create tp/sl orders with the qty you actually fill).
723 pub fn with_tpsl_mode(mut self, v: TpslMode) -> Self {
724 self.tpsl_mode = Some(v);
725 self
726 }
727 /// Set `tp_limit_price`: the limit order price when take profit price is triggered linear & inverse: only works when tpslMode=Partial and tpOrderType=Limit Spot(UTA): it is required when the order has takeProfit and "tpOrderType"=Limit.
728 pub fn with_tp_limit_price(mut self, v: Decimal) -> Self {
729 self.tp_limit_price = Some(v);
730 self
731 }
732 /// Set `sl_limit_price`: the limit order price when stop loss price is triggered linear & inverse: only works when tpslMode=Partial and slOrderType=Limit Spot(UTA): it is required when the order has stopLoss and "slOrderType"=Limit.
733 pub fn with_sl_limit_price(mut self, v: Decimal) -> Self {
734 self.sl_limit_price = Some(v);
735 self
736 }
737 /// Set `tp_order_type`: the order type when take profit is triggered linear & inverse: Market(default), Limit. For tpslMode=Full, it only supports tpOrderType=Market Spot(UTA): Market: when you set "takeProfit", Limit: when you set "takeProfit" and "tpLimitPrice".
738 pub fn with_tp_order_type(mut self, v: OrderType) -> Self {
739 self.tp_order_type = Some(v);
740 self
741 }
742 /// Set `sl_order_type`: the order type when stop loss is triggered linear & inverse: Market(default), Limit. For tpslMode=Full, it only supports slOrderType=Market Spot(UTA): Market: when you set "stopLoss", Limit: when you set "stopLoss" and "slLimitPrice".
743 pub fn with_sl_order_type(mut self, v: OrderType) -> Self {
744 self.sl_order_type = Some(v);
745 self
746 }
747 /// Set `bbo_side_type`: queue: use the order price on the orderbook in the same direction as the side Counterparty: use the order price on the orderbook in the opposite direction as the side Valid for linear & inverse.
748 pub fn with_bbo_side_type(mut self, v: impl Into<String>) -> Self {
749 self.bbo_side_type = Some(v.into());
750 self
751 }
752 /// Set `bbo_level`: 1,2,3,4,5 Valid for linear & inverse.
753 pub fn with_bbo_level(mut self, v: impl Into<String>) -> Self {
754 self.bbo_level = Some(v.into());
755 self
756 }
757}
758
759/// Result of `POST /v5/order/create` ([`Client::place_order`](crate::http::Client::place_order)).
760#[derive(Debug, Deserialize, PartialEq)]
761#[serde(rename_all = "camelCase")]
762pub struct PlaceOrderResponse {
763 /// Order ID
764 pub order_id: String,
765 /// User customised order ID
766 pub order_link_id: String,
767}
768
769/// Request body of `POST /v5/order/amend` ([`Client::amend_order`](crate::http::Client::amend_order)).
770#[derive(Debug, Serialize, Clone)]
771#[serde(rename_all = "camelCase")]
772pub struct AmendOrderRequest {
773 /// Product type linear, inverse, spot, option.
774 pub category: Category,
775 /// Symbol name, like BTCUSDT, uppercase only.
776 pub symbol: String,
777 /// Order ID. Either orderId or orderLinkId is required.
778 #[serde(skip_serializing_if = "Option::is_none")]
779 pub order_id: Option<String>,
780 /// User customised order ID. Either orderId or orderLinkId is required.
781 #[serde(skip_serializing_if = "Option::is_none")]
782 pub order_link_id: Option<String>,
783 /// Order quantity after modification. Do not pass it if not modify the qty.
784 #[serde(skip_serializing_if = "Option::is_none")]
785 pub qty: Option<Decimal>,
786 /// Order price after modification. Do not pass it if not modify the price.
787 #[serde(skip_serializing_if = "Option::is_none")]
788 pub price: Option<Decimal>,
789}
790
791impl AmendOrderRequest {
792 /// Create with the required fields; set the optional ones with the `with_*` methods.
793 pub fn new(category: Category, symbol: impl Into<String>) -> Self {
794 let symbol: String = symbol.into();
795 Self {
796 category,
797 symbol,
798 order_id: None,
799 order_link_id: None,
800 qty: None,
801 price: None,
802 }
803 }
804
805 /// Set `order_id`: order ID. Either orderId or orderLinkId is required.
806 pub fn with_order_id(mut self, v: impl Into<String>) -> Self {
807 self.order_id = Some(v.into());
808 self
809 }
810 /// Set `order_link_id`: user customised order ID. Either orderId or orderLinkId is required.
811 pub fn with_order_link_id(mut self, v: impl Into<String>) -> Self {
812 self.order_link_id = Some(v.into());
813 self
814 }
815 /// Set `qty`: order quantity after modification. Do not pass it if not modify the qty.
816 pub fn with_qty(mut self, v: Decimal) -> Self {
817 self.qty = Some(v);
818 self
819 }
820 /// Set `price`: order price after modification. Do not pass it if not modify the price.
821 pub fn with_price(mut self, v: Decimal) -> Self {
822 self.price = Some(v);
823 self
824 }
825}
826
827/// Result of `POST /v5/order/amend` ([`Client::amend_order`](crate::http::Client::amend_order)).
828#[derive(Debug, Deserialize, PartialEq)]
829#[serde(rename_all = "camelCase")]
830pub struct AmendOrderResponse {
831 /// Order ID
832 pub order_id: String,
833 #[serde(default, deserialize_with = "empty_string_as_none")]
834 /// User customised order ID
835 pub order_link_id: Option<String>,
836}
837
838/// Request body of `POST /v5/order/cancel` ([`Client::cancel_order`](crate::http::Client::cancel_order)).
839#[derive(Debug, Serialize, Clone)]
840#[serde(rename_all = "camelCase")]
841pub struct CancelOrderRequest {
842 /// Product type. linear, inverse, spot, option
843 pub category: Category,
844 /// Symbol name, like BTCUSDT, uppercase only
845 pub symbol: String,
846 /// Order ID. Either orderId or orderLinkId is required
847 #[serde(skip_serializing_if = "Option::is_none")]
848 pub order_id: Option<String>,
849 /// User customised order ID. Either orderId or orderLinkId is required
850 #[serde(skip_serializing_if = "Option::is_none")]
851 pub order_link_id: Option<String>,
852 /// Spot trading only
853 /// Order, tpslOrder, StopOrder
854 /// If not passed, Order by default
855 #[serde(skip_serializing_if = "Option::is_none")]
856 pub order_filter: Option<OrderFilter>,
857}
858
859impl CancelOrderRequest {
860 /// Create with the required fields; set the optional ones with the `with_*` methods.
861 pub fn new(category: Category, symbol: impl Into<String>) -> Self {
862 let symbol: String = symbol.into();
863 Self {
864 category,
865 symbol,
866 order_id: None,
867 order_link_id: None,
868 order_filter: None,
869 }
870 }
871
872 /// Set `order_id`: order ID. Either orderId or orderLinkId is required.
873 pub fn with_order_id(mut self, v: impl Into<String>) -> Self {
874 self.order_id = Some(v.into());
875 self
876 }
877 /// Set `order_link_id`: user customised order ID. Either orderId or orderLinkId is required.
878 pub fn with_order_link_id(mut self, v: impl Into<String>) -> Self {
879 self.order_link_id = Some(v.into());
880 self
881 }
882 /// Set `order_filter`: spot trading only Order, tpslOrder, StopOrder If not passed, Order by default.
883 pub fn with_order_filter(mut self, v: OrderFilter) -> Self {
884 self.order_filter = Some(v);
885 self
886 }
887}
888
889/// Result of `POST /v5/order/cancel` ([`Client::cancel_order`](crate::http::Client::cancel_order)).
890#[derive(Debug, Deserialize, PartialEq)]
891#[serde(rename_all = "camelCase")]
892pub struct CancelOrderResponse {
893 /// Order ID
894 pub order_id: String,
895 /// User customised order ID
896 #[serde(default, deserialize_with = "empty_string_as_none")]
897 pub order_link_id: Option<String>,
898}
899
900// ── Cancel All Orders ────────────────────────────────────────────────────────
901
902/// Request body of `POST /v5/order/cancel-all` ([`Client::cancel_all_orders`](crate::http::Client::cancel_all_orders)).
903#[derive(Debug, Serialize, Clone)]
904#[serde(rename_all = "camelCase")]
905pub struct CancelAllOrdersRequest {
906 /// Product type. linear, inverse, spot, option.
907 pub category: Category,
908 /// Symbol name, like BTCUSDT, uppercase only linear & inverse: Required if not passing baseCoin or settleCoin.
909 #[serde(skip_serializing_if = "Option::is_none")]
910 pub symbol: Option<String>,
911 /// Base coin, uppercase only. linear & inverse: If cancel all by baseCoin, it will cancel all of the corresponding category's orders. Required if not passing symbol or settleCoin.
912 #[serde(skip_serializing_if = "Option::is_none")]
913 pub base_coin: Option<String>,
914 /// Settle coin, uppercase only linear & inverse: Required if not passing symbol or baseCoin option: USDT or USDC Not support spot.
915 #[serde(skip_serializing_if = "Option::is_none")]
916 pub settle_coin: Option<String>,
917 /// Category= spot, you can pass Order, tpslOrder, StopOrder, OcoOrder, BidirectionalTpslOrder If not passed, Order by default category= linear or inverse, you can pass Order, StopOrder, OpenOrder If not passed, all kinds of orders will be ...
918 #[serde(skip_serializing_if = "Option::is_none")]
919 pub order_filter: Option<OrderFilter>,
920 /// For futures only: TakeProfit, StopLoss, TrailingStop
921 #[serde(skip_serializing_if = "Option::is_none")]
922 pub stop_order_type: Option<StopOrderType>,
923}
924
925impl CancelAllOrdersRequest {
926 /// Create with the required fields; set the optional ones with the `with_*` methods.
927 pub fn new(category: Category) -> Self {
928 Self {
929 category,
930 symbol: None,
931 base_coin: None,
932 settle_coin: None,
933 order_filter: None,
934 stop_order_type: None,
935 }
936 }
937
938 /// Set `symbol`: symbol name, like BTCUSDT, uppercase only linear & inverse: Required if not passing baseCoin or settleCoin.
939 pub fn with_symbol(mut self, v: impl Into<String>) -> Self {
940 self.symbol = Some(v.into());
941 self
942 }
943 /// Set `base_coin`: base coin, uppercase only. linear & inverse: If cancel all by baseCoin, it will cancel all of the corresponding category's orders. Required if not passing symbol or settleCoin.
944 pub fn with_base_coin(mut self, v: impl Into<String>) -> Self {
945 self.base_coin = Some(v.into());
946 self
947 }
948 /// Set `settle_coin`: settle coin, uppercase only linear & inverse: Required if not passing symbol or baseCoin option: USDT or USDC Not support spot.
949 pub fn with_settle_coin(mut self, v: impl Into<String>) -> Self {
950 self.settle_coin = Some(v.into());
951 self
952 }
953 /// Set `order_filter`: category= spot, you can pass Order, tpslOrder, StopOrder, OcoOrder, BidirectionalTpslOrder If not passed, Order by default category= linear or inverse, you can pass Order, StopOrder, OpenOrder If not passed, all kinds of orders will be ...
954 pub fn with_order_filter(mut self, v: OrderFilter) -> Self {
955 self.order_filter = Some(v);
956 self
957 }
958 /// Set `stop_order_type`: for futures only: TakeProfit, StopLoss, TrailingStop.
959 pub fn with_stop_order_type(mut self, v: StopOrderType) -> Self {
960 self.stop_order_type = Some(v);
961 self
962 }
963}
964
965/// Response for cancel-all: a flat list of cancelled order IDs.
966pub type CancelAllOrdersResponse = List<CancelOrderResponse>;
967
968// ── Order History ────────────────────────────────────────────────────────────
969
970/// Query parameters of `GET /v5/order/history` ([`Client::get_order_history`](crate::http::Client::get_order_history)).
971#[derive(Clone, Debug, Serialize)]
972#[serde(rename_all = "camelCase")]
973pub struct GetOrderHistoryParams {
974 /// Product type linear, inverse, spot, option.
975 pub category: Category,
976 /// Symbol name, like BTCUSDT, uppercase only.
977 #[serde(skip_serializing_if = "Option::is_none")]
978 pub symbol: Option<String>,
979 /// Base coin, uppercase only.
980 #[serde(skip_serializing_if = "Option::is_none")]
981 pub base_coin: Option<String>,
982 /// Settle coin, uppercase only.
983 #[serde(skip_serializing_if = "Option::is_none")]
984 pub settle_coin: Option<String>,
985 /// Order ID.
986 #[serde(skip_serializing_if = "Option::is_none")]
987 pub order_id: Option<String>,
988 /// User customised order ID.
989 #[serde(skip_serializing_if = "Option::is_none")]
990 pub order_link_id: Option<String>,
991 /// Order: active order StopOrder: conditional order for Futures and Spot tpslOrder: spot TP/SL order OcoOrder: spot OCO orders BidirectionalTpslOrder: Spot bidirectional TPSL order all kinds of orders are returned by default.
992 #[serde(skip_serializing_if = "Option::is_none")]
993 pub order_filter: Option<OrderFilter>,
994 /// Order status.
995 #[serde(skip_serializing_if = "Option::is_none")]
996 pub order_status: Option<OrderStatus>,
997 /// Start timestamp (ms)
998 #[serde(skip_serializing_if = "Option::is_none")]
999 pub start_time: Option<Timestamp>,
1000 /// End timestamp (ms)
1001 #[serde(skip_serializing_if = "Option::is_none")]
1002 pub end_time: Option<Timestamp>,
1003 /// [1, 50]. Default: 20
1004 #[serde(skip_serializing_if = "Option::is_none")]
1005 pub limit: Option<i32>,
1006 /// Cursor. Use the nextPageCursor token from the response to retrieve the next page of the result set.
1007 #[serde(skip_serializing_if = "Option::is_none")]
1008 pub cursor: Option<String>,
1009}
1010
1011impl GetOrderHistoryParams {
1012 /// Create with the required fields; set the optional ones with the `with_*` methods.
1013 pub fn new(category: Category) -> Self {
1014 Self {
1015 category,
1016 symbol: None,
1017 base_coin: None,
1018 settle_coin: None,
1019 order_id: None,
1020 order_link_id: None,
1021 order_filter: None,
1022 order_status: None,
1023 start_time: None,
1024 end_time: None,
1025 limit: None,
1026 cursor: None,
1027 }
1028 }
1029
1030 /// Set `symbol`: symbol name, like BTCUSDT, uppercase only.
1031 pub fn with_symbol(mut self, v: impl Into<String>) -> Self {
1032 self.symbol = Some(v.into());
1033 self
1034 }
1035 /// Set `base_coin`: base coin, uppercase only.
1036 pub fn with_base_coin(mut self, v: impl Into<String>) -> Self {
1037 self.base_coin = Some(v.into());
1038 self
1039 }
1040 /// Set `settle_coin`: settle coin, uppercase only.
1041 pub fn with_settle_coin(mut self, v: impl Into<String>) -> Self {
1042 self.settle_coin = Some(v.into());
1043 self
1044 }
1045 /// Set `order_id`: order ID.
1046 pub fn with_order_id(mut self, v: impl Into<String>) -> Self {
1047 self.order_id = Some(v.into());
1048 self
1049 }
1050 /// Set `order_link_id`: user customised order ID.
1051 pub fn with_order_link_id(mut self, v: impl Into<String>) -> Self {
1052 self.order_link_id = Some(v.into());
1053 self
1054 }
1055 /// Set `order_filter`: order: active order StopOrder: conditional order for Futures and Spot tpslOrder: spot TP/SL order OcoOrder: spot OCO orders BidirectionalTpslOrder: Spot bidirectional TPSL order all kinds of orders are returned by default.
1056 pub fn with_order_filter(mut self, v: OrderFilter) -> Self {
1057 self.order_filter = Some(v);
1058 self
1059 }
1060 /// Set `order_status`: order status.
1061 pub fn with_order_status(mut self, v: OrderStatus) -> Self {
1062 self.order_status = Some(v);
1063 self
1064 }
1065 /// Set `start_time`: start timestamp (ms).
1066 pub fn with_start_time(mut self, v: Timestamp) -> Self {
1067 self.start_time = Some(v);
1068 self
1069 }
1070 /// Set `end_time`: end timestamp (ms).
1071 pub fn with_end_time(mut self, v: Timestamp) -> Self {
1072 self.end_time = Some(v);
1073 self
1074 }
1075 /// Set `limit`: [1, 50]. Default: 20.
1076 pub fn with_limit(mut self, v: i32) -> Self {
1077 self.limit = Some(v);
1078 self
1079 }
1080 /// Set `cursor`: cursor. Use the nextPageCursor token from the response to retrieve the next page of the result set.
1081 pub fn with_cursor(mut self, v: impl Into<String>) -> Self {
1082 self.cursor = Some(v.into());
1083 self
1084 }
1085}
1086
1087// ── Batch Orders ─────────────────────────────────────────────────────────────
1088
1089/// Request body of `POST /v5/order/create-batch` ([`Client::place_orders_batch`](crate::http::Client::place_orders_batch)).
1090#[derive(Debug, Serialize, Clone)]
1091#[serde(rename_all = "camelCase")]
1092pub struct PlaceOrderBatchRequest {
1093 /// Product type linear, option, spot, inverse.
1094 pub category: Category,
1095 /// Orders to create (at most 20 for linear, 10 otherwise).
1096 pub request: Vec<PlaceOrderRequest>,
1097}
1098
1099impl PlaceOrderBatchRequest {
1100 /// Create with the required fields; set the optional ones with the `with_*` methods.
1101 pub fn new(category: Category, request: Vec<PlaceOrderRequest>) -> Self {
1102 Self { category, request }
1103 }
1104}
1105
1106/// Request body of `POST /v5/order/amend-batch` ([`Client::amend_orders_batch`](crate::http::Client::amend_orders_batch)).
1107#[derive(Debug, Serialize, Clone)]
1108#[serde(rename_all = "camelCase")]
1109pub struct AmendOrderBatchRequest {
1110 /// Product type linear, option, spot, inverse.
1111 pub category: Category,
1112 /// Orders to amend.
1113 pub request: Vec<AmendOrderRequest>,
1114}
1115
1116impl AmendOrderBatchRequest {
1117 /// Create with the required fields; set the optional ones with the `with_*` methods.
1118 pub fn new(category: Category, request: Vec<AmendOrderRequest>) -> Self {
1119 Self { category, request }
1120 }
1121}
1122
1123/// Request body of `POST /v5/order/cancel-batch` ([`Client::cancel_orders_batch`](crate::http::Client::cancel_orders_batch)).
1124#[derive(Debug, Serialize, Clone)]
1125#[serde(rename_all = "camelCase")]
1126pub struct CancelOrderBatchRequest {
1127 /// Product type linear, option, spot, inverse.
1128 pub category: Category,
1129 /// Orders to cancel.
1130 pub request: Vec<CancelOrderRequest>,
1131}
1132
1133impl CancelOrderBatchRequest {
1134 /// Create with the required fields; set the optional ones with the `with_*` methods.
1135 pub fn new(category: Category, request: Vec<CancelOrderRequest>) -> Self {
1136 Self { category, request }
1137 }
1138}
1139
1140/// Item of the list returned by `POST /v5/order/create-batch` ([`Client::place_orders_batch`](crate::http::Client::place_orders_batch)).
1141#[derive(Debug, Deserialize, PartialEq)]
1142#[serde(rename_all = "camelCase")]
1143pub struct PlaceOrderBatchResult {
1144 /// Product type.
1145 pub category: Category,
1146 /// Symbol name.
1147 pub symbol: String,
1148 /// Order ID.
1149 pub order_id: String,
1150 /// User customised order ID.
1151 #[serde(default, deserialize_with = "empty_string_as_none")]
1152 pub order_link_id: Option<String>,
1153 /// Order creation timestamp (ms). Note: Bybit uses the non-standard key "createAt".
1154 #[serde(rename = "createAt", default)]
1155 pub create_at: Option<Timestamp>,
1156}
1157
1158/// Item of the list returned by `POST /v5/order/amend-batch` ([`Client::amend_orders_batch`](crate::http::Client::amend_orders_batch)).
1159#[derive(Debug, Deserialize, PartialEq)]
1160#[serde(rename_all = "camelCase")]
1161pub struct AmendOrderBatchResult {
1162 /// Product type.
1163 pub category: Category,
1164 /// Symbol name.
1165 pub symbol: String,
1166 /// Order ID.
1167 pub order_id: String,
1168 /// User customised order ID.
1169 #[serde(default, deserialize_with = "empty_string_as_none")]
1170 pub order_link_id: Option<String>,
1171}
1172
1173/// Item of the list returned by `POST /v5/order/cancel-batch` ([`Client::cancel_orders_batch`](crate::http::Client::cancel_orders_batch)).
1174#[derive(Debug, Deserialize, PartialEq)]
1175#[serde(rename_all = "camelCase")]
1176pub struct CancelOrderBatchResult {
1177 /// Product type.
1178 pub category: Category,
1179 /// Symbol name.
1180 pub symbol: String,
1181 /// Order ID.
1182 pub order_id: String,
1183 /// User customised order ID.
1184 #[serde(default, deserialize_with = "empty_string_as_none")]
1185 pub order_link_id: Option<String>,
1186}
1187
1188// --- Spot Borrow Check ---
1189
1190/// Query params for [`Client::get_spot_borrow_check`](crate::http::Client::get_spot_borrow_check).
1191///
1192/// Covers: spot only
1193#[derive(Debug, Serialize, Clone)]
1194pub struct GetSpotBorrowCheckParams {
1195 /// Product type spot.
1196 pub category: Category,
1197 /// Symbol name.
1198 pub symbol: String,
1199 /// Transaction side. Buy, Sell.
1200 pub side: Side,
1201}
1202
1203impl GetSpotBorrowCheckParams {
1204 /// Create with the required fields; set the optional ones with the `with_*` methods.
1205 pub fn new(category: Category, symbol: impl Into<String>, side: Side) -> Self {
1206 Self {
1207 category,
1208 symbol: symbol.into(),
1209 side,
1210 }
1211 }
1212}
1213
1214/// Response for [`Client::get_spot_borrow_check`](crate::http::Client::get_spot_borrow_check).
1215#[derive(Debug, Deserialize, PartialEq)]
1216#[serde(rename_all = "camelCase")]
1217pub struct SpotBorrowCheck {
1218 /// Symbol name, like BTCUSDT, uppercase only.
1219 pub symbol: String,
1220 /// Side.
1221 pub side: Side,
1222 /// Maximum tradeable quantity with leverage (in base coin).
1223 pub max_trade_qty: Decimal,
1224 /// Maximum tradeable amount with leverage (in quote coin).
1225 pub max_trade_amount: Decimal,
1226 /// Maximum tradeable quantity without leverage (in base coin).
1227 pub spot_max_trade_qty: Decimal,
1228 /// Maximum tradeable amount without leverage (in quote coin).
1229 pub spot_max_trade_amount: Decimal,
1230 /// The coin to borrow.
1231 pub borrow_coin: String,
1232}