1use std::collections::BTreeMap;
2use std::fmt::Display;
3use crate::util::build_signed_request;
4use crate::errors::Result;
5use crate::client::Client;
6use crate::api::{API, Futures};
7use crate::model::Empty;
8use crate::account::OrderSide;
9use crate::futures::model::{Order, TradeHistory};
10
11use super::model::{
12 ChangeLeverageResponse, Transaction, CanceledOrder, PositionRisk, AccountBalance,
13 AccountInformation,
14};
15
16#[derive(Clone)]
17pub struct FuturesAccount {
18 pub client: Client,
19 pub recv_window: u64,
20}
21
22pub enum ContractType {
23 Perpetual,
24 CurrentMonth,
25 NextMonth,
26 CurrentQuarter,
27 NextQuarter,
28}
29
30impl From<ContractType> for String {
31 fn from(item: ContractType) -> Self {
32 match item {
33 ContractType::Perpetual => String::from("PERPETUAL"),
34 ContractType::CurrentMonth => String::from("CURRENT_MONTH"),
35 ContractType::NextMonth => String::from("NEXT_MONTH"),
36 ContractType::CurrentQuarter => String::from("CURRENT_QUARTER"),
37 ContractType::NextQuarter => String::from("NEXT_QUARTER"),
38 }
39 }
40}
41
42pub enum PositionSide {
43 Both,
44 Long,
45 Short,
46}
47
48impl Display for PositionSide {
49 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
50 match self {
51 Self::Both => write!(f, "BOTH"),
52 Self::Long => write!(f, "LONG"),
53 Self::Short => write!(f, "SHORT"),
54 }
55 }
56}
57
58pub enum OrderType {
59 Limit,
60 Market,
61 Stop,
62 StopMarket,
63 TakeProfit,
64 TakeProfitMarket,
65 TrailingStopMarket,
66}
67
68impl Display for OrderType {
69 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
70 match self {
71 Self::Limit => write!(f, "LIMIT"),
72 Self::Market => write!(f, "MARKET"),
73 Self::Stop => write!(f, "STOP"),
74 Self::StopMarket => write!(f, "STOP_MARKET"),
75 Self::TakeProfit => write!(f, "TAKE_PROFIT"),
76 Self::TakeProfitMarket => write!(f, "TAKE_PROFIT_MARKET"),
77 Self::TrailingStopMarket => write!(f, "TRAILING_STOP_MARKET"),
78 }
79 }
80}
81
82pub enum WorkingType {
83 MarkPrice,
84 ContractPrice,
85}
86
87impl Display for WorkingType {
88 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
89 match self {
90 Self::MarkPrice => write!(f, "MARK_PRICE"),
91 Self::ContractPrice => write!(f, "CONTRACT_PRICE"),
92 }
93 }
94}
95
96#[allow(clippy::all)]
97pub enum TimeInForce {
98 GTC,
99 IOC,
100 FOK,
101 GTX,
102}
103
104impl Display for TimeInForce {
105 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
106 match self {
107 Self::GTC => write!(f, "GTC"),
108 Self::IOC => write!(f, "IOC"),
109 Self::FOK => write!(f, "FOK"),
110 Self::GTX => write!(f, "GTX"),
111 }
112 }
113}
114
115struct OrderRequest {
116 pub symbol: String,
117 pub side: OrderSide,
118 pub position_side: Option<PositionSide>,
119 pub order_type: OrderType,
120 pub time_in_force: Option<TimeInForce>,
121 pub qty: Option<f64>,
122 pub reduce_only: Option<bool>,
123 pub price: Option<f64>,
124 pub stop_price: Option<f64>,
125 pub close_position: Option<bool>,
126 pub activation_price: Option<f64>,
127 pub callback_rate: Option<f64>,
128 pub working_type: Option<WorkingType>,
129 pub price_protect: Option<f64>,
130}
131
132pub struct CustomOrderRequest {
133 pub symbol: String,
134 pub side: OrderSide,
135 pub position_side: Option<PositionSide>,
136 pub order_type: OrderType,
137 pub time_in_force: Option<TimeInForce>,
138 pub qty: Option<f64>,
139 pub reduce_only: Option<bool>,
140 pub price: Option<f64>,
141 pub stop_price: Option<f64>,
142 pub close_position: Option<bool>,
143 pub activation_price: Option<f64>,
144 pub callback_rate: Option<f64>,
145 pub working_type: Option<WorkingType>,
146 pub price_protect: Option<f64>,
147}
148
149pub struct IncomeRequest {
150 pub symbol: Option<String>,
151 pub income_type: Option<IncomeType>,
152 pub start_time: Option<u64>,
153 pub end_time: Option<u64>,
154 pub limit: Option<u32>,
155}
156
157#[allow(non_camel_case_types)]
158pub enum IncomeType {
159 TRANSFER,
160 WELCOME_BONUS,
161 REALIZED_PNL,
162 FUNDING_FEE,
163 COMMISSION,
164 INSURANCE_CLEAR,
165 REFERRAL_KICKBACK,
166 COMMISSION_REBATE,
167 API_REBATE,
168 CONTEST_REWARD,
169 CROSS_COLLATERAL_TRANSFER,
170 OPTIONS_PREMIUM_FEE,
171 OPTIONS_SETTLE_PROFIT,
172 INTERNAL_TRANSFER,
173 AUTO_EXCHANGE,
174 DELIVERED_SETTELMENT,
175 COIN_SWAP_DEPOSIT,
176 COIN_SWAP_WITHDRAW,
177 POSITION_LIMIT_INCREASE_FEE,
178}
179
180impl Display for IncomeType {
181 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
182 match self {
183 Self::TRANSFER => write!(f, "TRANSFER"),
184 Self::WELCOME_BONUS => write!(f, "WELCOME_BONUS"),
185 Self::REALIZED_PNL => write!(f, "REALIZED_PNL"),
186 Self::FUNDING_FEE => write!(f, "FUNDING_FEE"),
187 Self::COMMISSION => write!(f, "COMMISSION"),
188 Self::INSURANCE_CLEAR => write!(f, "INSURANCE_CLEAR"),
189 Self::REFERRAL_KICKBACK => write!(f, "REFERRAL_KICKBACK"),
190 Self::COMMISSION_REBATE => write!(f, "COMMISSION_REBATE"),
191 Self::API_REBATE => write!(f, "API_REBATE"),
192 Self::CONTEST_REWARD => write!(f, "CONTEST_REWARD"),
193 Self::CROSS_COLLATERAL_TRANSFER => write!(f, "CROSS_COLLATERAL_TRANSFER"),
194 Self::OPTIONS_PREMIUM_FEE => write!(f, "OPTIONS_PREMIUM_FEE"),
195 Self::OPTIONS_SETTLE_PROFIT => write!(f, "OPTIONS_SETTLE_PROFIT"),
196 Self::INTERNAL_TRANSFER => write!(f, "INTERNAL_TRANSFER"),
197 Self::AUTO_EXCHANGE => write!(f, "AUTO_EXCHANGE"),
198 Self::DELIVERED_SETTELMENT => write!(f, "DELIVERED_SETTELMENT"),
199 Self::COIN_SWAP_DEPOSIT => write!(f, "COIN_SWAP_DEPOSIT"),
200 Self::COIN_SWAP_WITHDRAW => write!(f, "COIN_SWAP_WITHDRAW"),
201 Self::POSITION_LIMIT_INCREASE_FEE => write!(f, "POSITION_LIMIT_INCREASE_FEE"),
202 }
203 }
204}
205
206impl FuturesAccount {
207 pub fn limit_buy(
208 &self, symbol: impl Into<String>, qty: impl Into<f64>, price: f64,
209 time_in_force: TimeInForce,
210 ) -> Result<Transaction> {
211 let buy = OrderRequest {
212 symbol: symbol.into(),
213 side: OrderSide::Buy,
214 position_side: None,
215 order_type: OrderType::Limit,
216 time_in_force: Some(time_in_force),
217 qty: Some(qty.into()),
218 reduce_only: None,
219 price: Some(price),
220 stop_price: None,
221 close_position: None,
222 activation_price: None,
223 callback_rate: None,
224 working_type: None,
225 price_protect: None,
226 };
227 let order = self.build_order(buy);
228 let request = build_signed_request(order, self.recv_window)?;
229 self.client
230 .post_signed(API::Futures(Futures::Order), request)
231 }
232
233 pub fn limit_sell(
234 &self, symbol: impl Into<String>, qty: impl Into<f64>, price: f64,
235 time_in_force: TimeInForce,
236 ) -> Result<Transaction> {
237 let sell = OrderRequest {
238 symbol: symbol.into(),
239 side: OrderSide::Sell,
240 position_side: None,
241 order_type: OrderType::Limit,
242 time_in_force: Some(time_in_force),
243 qty: Some(qty.into()),
244 reduce_only: None,
245 price: Some(price),
246 stop_price: None,
247 close_position: None,
248 activation_price: None,
249 callback_rate: None,
250 working_type: None,
251 price_protect: None,
252 };
253 let order = self.build_order(sell);
254 let request = build_signed_request(order, self.recv_window)?;
255 self.client
256 .post_signed(API::Futures(Futures::Order), request)
257 }
258
259 pub fn market_buy<S, F>(&self, symbol: S, qty: F) -> Result<Transaction>
261 where
262 S: Into<String>,
263 F: Into<f64>,
264 {
265 let buy = OrderRequest {
266 symbol: symbol.into(),
267 side: OrderSide::Buy,
268 position_side: None,
269 order_type: OrderType::Market,
270 time_in_force: None,
271 qty: Some(qty.into()),
272 reduce_only: None,
273 price: None,
274 stop_price: None,
275 close_position: None,
276 activation_price: None,
277 callback_rate: None,
278 working_type: None,
279 price_protect: None,
280 };
281 let order = self.build_order(buy);
282 let request = build_signed_request(order, self.recv_window)?;
283 self.client
284 .post_signed(API::Futures(Futures::Order), request)
285 }
286
287 pub fn market_sell<S, F>(&self, symbol: S, qty: F) -> Result<Transaction>
289 where
290 S: Into<String>,
291 F: Into<f64>,
292 {
293 let sell = OrderRequest {
294 symbol: symbol.into(),
295 side: OrderSide::Sell,
296 position_side: None,
297 order_type: OrderType::Market,
298 time_in_force: None,
299 qty: Some(qty.into()),
300 reduce_only: None,
301 price: None,
302 stop_price: None,
303 close_position: None,
304 activation_price: None,
305 callback_rate: None,
306 working_type: None,
307 price_protect: None,
308 };
309 let order = self.build_order(sell);
310 let request = build_signed_request(order, self.recv_window)?;
311 self.client
312 .post_signed(API::Futures(Futures::Order), request)
313 }
314
315 pub fn cancel_order<S>(&self, symbol: S, order_id: u64) -> Result<CanceledOrder>
316 where
317 S: Into<String>,
318 {
319 let mut parameters = BTreeMap::new();
320 parameters.insert("symbol".into(), symbol.into());
321 parameters.insert("orderId".into(), order_id.to_string());
322
323 let request = build_signed_request(parameters, self.recv_window)?;
324 self.client
325 .delete_signed(API::Futures(Futures::Order), Some(request))
326 }
327
328 pub fn cancel_order_with_client_id<S>(
329 &self, symbol: S, orig_client_order_id: String,
330 ) -> Result<CanceledOrder>
331 where
332 S: Into<String>,
333 {
334 let mut parameters = BTreeMap::new();
335 parameters.insert("symbol".into(), symbol.into());
336 parameters.insert("origClientOrderId".into(), orig_client_order_id);
337
338 let request = build_signed_request(parameters, self.recv_window)?;
339 self.client
340 .delete_signed(API::Futures(Futures::Order), Some(request))
341 }
342
343 pub fn stop_market_close_buy<S, F>(&self, symbol: S, stop_price: F) -> Result<Transaction>
345 where
346 S: Into<String>,
347 F: Into<f64>,
348 {
349 let sell = OrderRequest {
350 symbol: symbol.into(),
351 side: OrderSide::Buy,
352 position_side: None,
353 order_type: OrderType::StopMarket,
354 time_in_force: None,
355 qty: None,
356 reduce_only: None,
357 price: None,
358 stop_price: Some(stop_price.into()),
359 close_position: Some(true),
360 activation_price: None,
361 callback_rate: None,
362 working_type: None,
363 price_protect: None,
364 };
365 let order = self.build_order(sell);
366 let request = build_signed_request(order, self.recv_window)?;
367 self.client
368 .post_signed(API::Futures(Futures::Order), request)
369 }
370
371 pub fn stop_market_close_sell<S, F>(&self, symbol: S, stop_price: F) -> Result<Transaction>
373 where
374 S: Into<String>,
375 F: Into<f64>,
376 {
377 let sell = OrderRequest {
378 symbol: symbol.into(),
379 side: OrderSide::Sell,
380 position_side: None,
381 order_type: OrderType::StopMarket,
382 time_in_force: None,
383 qty: None,
384 reduce_only: None,
385 price: None,
386 stop_price: Some(stop_price.into()),
387 close_position: Some(true),
388 activation_price: None,
389 callback_rate: None,
390 working_type: None,
391 price_protect: None,
392 };
393 let order = self.build_order(sell);
394 let request = build_signed_request(order, self.recv_window)?;
395 self.client
396 .post_signed(API::Futures(Futures::Order), request)
397 }
398
399 pub fn custom_order(&self, order_request: CustomOrderRequest) -> Result<Transaction> {
401 let order = OrderRequest {
402 symbol: order_request.symbol,
403 side: order_request.side,
404 position_side: order_request.position_side,
405 order_type: order_request.order_type,
406 time_in_force: order_request.time_in_force,
407 qty: order_request.qty,
408 reduce_only: order_request.reduce_only,
409 price: order_request.price,
410 stop_price: order_request.stop_price,
411 close_position: order_request.close_position,
412 activation_price: order_request.activation_price,
413 callback_rate: order_request.callback_rate,
414 working_type: order_request.working_type,
415 price_protect: order_request.price_protect,
416 };
417 let order = self.build_order(order);
418 let request = build_signed_request(order, self.recv_window)?;
419 self.client
420 .post_signed(API::Futures(Futures::Order), request)
421 }
422
423 pub fn custom_batch_orders(
425 &self, _order_count: u64, order_requests: Vec<CustomOrderRequest>,
426 ) -> Result<Transaction> {
427 let request = String::new();
428 for order_request in order_requests {
429 let order = OrderRequest {
430 symbol: order_request.symbol,
431 side: order_request.side,
432 position_side: order_request.position_side,
433 order_type: order_request.order_type,
434 time_in_force: order_request.time_in_force,
435 qty: order_request.qty,
436 reduce_only: order_request.reduce_only,
437 price: order_request.price,
438 stop_price: order_request.stop_price,
439 close_position: order_request.close_position,
440 activation_price: order_request.activation_price,
441 callback_rate: order_request.callback_rate,
442 working_type: order_request.working_type,
443 price_protect: order_request.price_protect,
444 };
445 let _order = self.build_order(order);
446 }
449 self.client
450 .post_signed(API::Futures(Futures::Order), request)
451 }
452
453 pub fn get_all_orders<S, F, N>(
454 &self, symbol: S, order_id: F, start_time: F, end_time: F, limit: N,
455 ) -> Result<Vec<Order>>
456 where
457 S: Into<String>,
458 F: Into<Option<u64>>,
459 N: Into<Option<u16>>,
460 {
461 let mut parameters = BTreeMap::new();
462 parameters.insert("symbol".into(), symbol.into());
463 if let Some(order_id) = order_id.into() {
464 parameters.insert("orderId".into(), order_id.to_string());
465 }
466 if let Some(start_time) = start_time.into() {
467 parameters.insert("startTime".into(), start_time.to_string());
468 }
469 if let Some(end_time) = end_time.into() {
470 parameters.insert("endTime".into(), end_time.to_string());
471 }
472 if let Some(limit) = limit.into() {
473 parameters.insert("limit".into(), limit.to_string());
474 }
475
476 let request = build_signed_request(parameters, self.recv_window)?;
477 self.client
478 .get_signed(API::Futures(Futures::AllOrders), Some(request))
479 }
480
481 pub fn get_user_trades<S, F, N>(
482 &self, symbol: S, from_id: F, start_time: F, end_time: F, limit: N,
483 ) -> Result<Vec<TradeHistory>>
484 where
485 S: Into<String>,
486 F: Into<Option<u64>>,
487 N: Into<Option<u16>>,
488 {
489 let mut parameters = BTreeMap::new();
490 parameters.insert("symbol".into(), symbol.into());
491 if let Some(order_id) = from_id.into() {
492 parameters.insert("fromId".into(), order_id.to_string());
493 }
494 if let Some(start_time) = start_time.into() {
495 parameters.insert("startTime".into(), start_time.to_string());
496 }
497 if let Some(end_time) = end_time.into() {
498 parameters.insert("endTime".into(), end_time.to_string());
499 }
500 if let Some(limit) = limit.into() {
501 parameters.insert("limit".into(), limit.to_string());
502 }
503
504 let request = build_signed_request(parameters, self.recv_window)?;
505 self.client
506 .get_signed(API::Futures(Futures::UserTrades), Some(request))
507 }
508 fn build_order(&self, order: OrderRequest) -> BTreeMap<String, String> {
509 let mut parameters = BTreeMap::new();
510 parameters.insert("symbol".into(), order.symbol);
511 parameters.insert("side".into(), order.side.to_string());
512 parameters.insert("type".into(), order.order_type.to_string());
513
514 if let Some(position_side) = order.position_side {
515 parameters.insert("positionSide".into(), position_side.to_string());
516 }
517 if let Some(time_in_force) = order.time_in_force {
518 parameters.insert("timeInForce".into(), time_in_force.to_string());
519 }
520 if let Some(qty) = order.qty {
521 parameters.insert("quantity".into(), qty.to_string());
522 }
523 if let Some(reduce_only) = order.reduce_only {
524 parameters.insert("reduceOnly".into(), reduce_only.to_string().to_uppercase());
525 }
526 if let Some(price) = order.price {
527 parameters.insert("price".into(), price.to_string());
528 }
529 if let Some(stop_price) = order.stop_price {
530 parameters.insert("stopPrice".into(), stop_price.to_string());
531 }
532 if let Some(close_position) = order.close_position {
533 parameters.insert(
534 "closePosition".into(),
535 close_position.to_string().to_uppercase(),
536 );
537 }
538 if let Some(activation_price) = order.activation_price {
539 parameters.insert("activationPrice".into(), activation_price.to_string());
540 }
541 if let Some(callback_rate) = order.callback_rate {
542 parameters.insert("callbackRate".into(), callback_rate.to_string());
543 }
544 if let Some(working_type) = order.working_type {
545 parameters.insert("workingType".into(), working_type.to_string());
546 }
547 if let Some(price_protect) = order.price_protect {
548 parameters.insert(
549 "priceProtect".into(),
550 price_protect.to_string().to_uppercase(),
551 );
552 }
553
554 parameters
555 }
556
557 pub fn position_information<S>(&self, symbol: S) -> Result<Vec<PositionRisk>>
558 where
559 S: Into<String>,
560 {
561 let mut parameters = BTreeMap::new();
562 parameters.insert("symbol".into(), symbol.into());
563
564 let request = build_signed_request(parameters, self.recv_window)?;
565 self.client
566 .get_signed(API::Futures(Futures::PositionRisk), Some(request))
567 }
568
569 pub fn account_information(&self) -> Result<AccountInformation> {
570 let parameters = BTreeMap::new();
571
572 let request = build_signed_request(parameters, self.recv_window)?;
573 self.client
574 .get_signed(API::Futures(Futures::Account), Some(request))
575 }
576
577 pub fn account_balance(&self) -> Result<Vec<AccountBalance>> {
578 let parameters = BTreeMap::new();
579
580 let request = build_signed_request(parameters, self.recv_window)?;
581 self.client
582 .get_signed(API::Futures(Futures::Balance), Some(request))
583 }
584
585 pub fn change_initial_leverage<S>(
586 &self, symbol: S, leverage: u8,
587 ) -> Result<ChangeLeverageResponse>
588 where
589 S: Into<String>,
590 {
591 let mut parameters: BTreeMap<String, String> = BTreeMap::new();
592 parameters.insert("symbol".into(), symbol.into());
593 parameters.insert("leverage".into(), leverage.to_string());
594
595 let request = build_signed_request(parameters, self.recv_window)?;
596 self.client
597 .post_signed(API::Futures(Futures::ChangeInitialLeverage), request)
598 }
599
600 pub fn change_margin_type<S>(&self, symbol: S, isolated: bool) -> Result<()>
601 where
602 S: Into<String>,
603 {
604 let mut parameters: BTreeMap<String, String> = BTreeMap::new();
605 let margin_type = if isolated { "ISOLATED" } else { "CROSSED" };
606 parameters.insert("symbol".into(), symbol.into());
607 parameters.insert("marginType".into(), margin_type.into());
608
609 let request = build_signed_request(parameters, self.recv_window)?;
610 self.client
611 .post_signed::<Empty>(API::Futures(Futures::MarginType), request)
612 .map(|_| ())
613 }
614
615 pub fn change_position_margin<S>(
616 &self, symbol: S, amount: f64, is_adding_margin: bool,
617 ) -> Result<()>
618 where
619 S: Into<String>,
620 {
621 let mut parameters: BTreeMap<String, String> = BTreeMap::new();
622 let margin = if is_adding_margin { "1" } else { "2" };
623 parameters.insert("symbol".into(), symbol.into());
624 parameters.insert("amount".into(), amount.to_string());
625 parameters.insert("type".into(), margin.into());
626
627 let request = build_signed_request(parameters, self.recv_window)?;
628 self.client
629 .post_signed::<Empty>(API::Futures(Futures::PositionMargin), request)
630 .map(|_| ())
631 }
632
633 pub fn change_position_mode(&self, dual_side_position: bool) -> Result<()> {
634 let mut parameters: BTreeMap<String, String> = BTreeMap::new();
635 let dual_side = if dual_side_position { "true" } else { "false" };
636 parameters.insert("dualSidePosition".into(), dual_side.into());
637
638 let request = build_signed_request(parameters, self.recv_window)?;
639 self.client
640 .post_signed::<Empty>(API::Futures(Futures::PositionSide), request)
641 .map(|_| ())
642 }
643
644 pub fn cancel_all_open_orders<S>(&self, symbol: S) -> Result<()>
645 where
646 S: Into<String>,
647 {
648 let mut parameters: BTreeMap<String, String> = BTreeMap::new();
649 parameters.insert("symbol".into(), symbol.into());
650 let request = build_signed_request(parameters, self.recv_window)?;
651 self.client
652 .delete_signed::<Empty>(API::Futures(Futures::AllOpenOrders), Some(request))
653 .map(|_| ())
654 }
655
656 pub fn get_all_open_orders<S>(&self, symbol: S) -> Result<Vec<crate::futures::model::Order>>
657 where
658 S: Into<String>,
659 {
660 let mut parameters: BTreeMap<String, String> = BTreeMap::new();
661 parameters.insert("symbol".into(), symbol.into());
662 let request = build_signed_request(parameters, self.recv_window)?;
663 self.client
664 .get_signed(API::Futures(Futures::OpenOrders), Some(request))
665 }
666
667 pub fn get_income(
668 &self, income_request: IncomeRequest,
669 ) -> Result<Vec<crate::futures::model::Income>> {
670 let mut parameters: BTreeMap<String, String> = BTreeMap::new();
671 if let Some(symbol) = income_request.symbol {
672 parameters.insert("symbol".into(), symbol);
673 }
674 if let Some(income_type) = income_request.income_type {
675 parameters.insert("incomeType".into(), income_type.to_string());
676 }
677 if let Some(start_time) = income_request.start_time {
678 parameters.insert("startTime".into(), start_time.to_string());
679 }
680 if let Some(end_time) = income_request.end_time {
681 parameters.insert("endTime".into(), end_time.to_string());
682 }
683 if let Some(limit) = income_request.limit {
684 parameters.insert("limit".into(), limit.to_string());
685 }
686
687 let request = build_signed_request(parameters, self.recv_window)?;
688 println!("{}", request);
689 self.client
690 .get_signed(API::Futures(Futures::Income), Some(request))
691 }
692}