1use crate::DeribitHttpClient;
4use crate::constants::endpoints::*;
5use crate::error::HttpError;
6use crate::model::account::Subaccount;
7use crate::model::position::Position;
8use crate::model::request::mass_quote::MassQuoteRequest;
9use crate::model::request::order::OrderRequest;
10use crate::model::request::trade::TradesRequest;
11use crate::model::response::api_response::ApiResponse;
12use crate::model::response::deposit::DepositsResponse;
13use crate::model::response::mass_quote::MassQuoteResponse;
14use crate::model::response::order::{OrderInfoResponse, OrderResponse};
15use crate::model::response::other::{
16 AccountSummaryResponse, TransactionLogResponse, TransferResultResponse,
17};
18use crate::model::response::withdrawal::WithdrawalsResponse;
19use crate::model::{
20 TransactionLogRequest, UserTradeResponseByOrder, UserTradeWithPaginationResponse,
21};
22use crate::prelude::Trigger;
23
24impl DeribitHttpClient {
26 pub async fn get_subaccounts(
44 &self,
45 with_portfolio: Option<bool>,
46 ) -> Result<Vec<Subaccount>, HttpError> {
47 let mut query_params = Vec::new();
48
49 if let Some(with_portfolio) = with_portfolio {
50 query_params.push(("with_portfolio".to_string(), with_portfolio.to_string()));
51 }
52
53 let query_string = if query_params.is_empty() {
54 String::new()
55 } else {
56 "?".to_string()
57 + &query_params
58 .iter()
59 .map(|(k, v)| format!("{}={}", k, urlencoding::encode(v)))
60 .collect::<Vec<_>>()
61 .join("&")
62 };
63
64 let url = format!("{}{}{}", self.base_url(), GET_SUBACCOUNTS, query_string);
65
66 let response = self.make_authenticated_request(&url).await?;
67
68 if !response.status().is_success() {
69 let error_text = response
70 .text()
71 .await
72 .unwrap_or_else(|_| "Unknown error".to_string());
73 return Err(HttpError::RequestFailed(format!(
74 "Get subaccounts failed: {}",
75 error_text
76 )));
77 }
78
79 let response_text = response.text().await.map_err(|e| {
81 HttpError::InvalidResponse(format!("Failed to read response text: {}", e))
82 })?;
83
84 tracing::debug!("Raw API response: {}", response_text);
85
86 let api_response: ApiResponse<Vec<Subaccount>> = serde_json::from_str(&response_text)
87 .map_err(|e| {
88 HttpError::InvalidResponse(format!(
89 "Failed to parse JSON: {} - Raw response: {}",
90 e, response_text
91 ))
92 })?;
93
94 if let Some(error) = api_response.error {
95 return Err(HttpError::RequestFailed(format!(
96 "API error: {} - {}",
97 error.code, error.message
98 )));
99 }
100
101 api_response.result.ok_or_else(|| {
102 HttpError::InvalidResponse("No subaccounts data in response".to_string())
103 })
104 }
105
106 pub async fn get_transaction_log(
130 &self,
131 request: TransactionLogRequest,
132 ) -> Result<TransactionLogResponse, HttpError> {
133 let mut query_params = vec![("currency".to_string(), request.currency.to_string())];
134
135 query_params.push((
136 "start_timestamp".to_string(),
137 request.start_timestamp.to_string(),
138 ));
139 query_params.push((
140 "end_timestamp".to_string(),
141 request.end_timestamp.to_string(),
142 ));
143
144 if let Some(query) = request.query {
145 query_params.push(("query".to_string(), query));
146 }
147
148 if let Some(count) = request.count {
149 query_params.push(("count".to_string(), count.to_string()));
150 }
151
152 if let Some(subaccount_id) = request.subaccount_id {
153 query_params.push(("subaccount_id".to_string(), subaccount_id.to_string()));
154 }
155
156 if let Some(continuation) = request.continuation {
157 query_params.push(("continuation".to_string(), continuation.to_string()));
158 }
159
160 let query_string = query_params
161 .iter()
162 .map(|(k, v)| format!("{}={}", k, urlencoding::encode(v)))
163 .collect::<Vec<_>>()
164 .join("&");
165
166 let url = format!(
167 "{}{}?{}",
168 self.base_url(),
169 GET_TRANSACTION_LOG,
170 query_string
171 );
172
173 let response = self.make_authenticated_request(&url).await?;
174
175 if !response.status().is_success() {
176 let error_text = response
177 .text()
178 .await
179 .unwrap_or_else(|_| "Unknown error".to_string());
180 return Err(HttpError::RequestFailed(format!(
181 "Get transaction log failed: {}",
182 error_text
183 )));
184 }
185
186 let api_response: ApiResponse<TransactionLogResponse> = response
187 .json()
188 .await
189 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
190
191 if let Some(error) = api_response.error {
192 return Err(HttpError::RequestFailed(format!(
193 "API error: {} - {}",
194 error.code, error.message
195 )));
196 }
197
198 api_response.result.ok_or_else(|| {
199 HttpError::InvalidResponse("No transaction log data in response".to_string())
200 })
201 }
202
203 pub async fn get_deposits(
223 &self,
224 currency: &str,
225 count: Option<u32>,
226 offset: Option<u32>,
227 ) -> Result<DepositsResponse, HttpError> {
228 let mut query_params = vec![("currency".to_string(), currency.to_string())];
229
230 if let Some(count) = count {
231 query_params.push(("count".to_string(), count.to_string()));
232 }
233
234 if let Some(offset) = offset {
235 query_params.push(("offset".to_string(), offset.to_string()));
236 }
237
238 let query_string = query_params
239 .iter()
240 .map(|(k, v)| format!("{}={}", k, urlencoding::encode(v)))
241 .collect::<Vec<_>>()
242 .join("&");
243
244 let url = format!("{}{}?{}", self.base_url(), GET_DEPOSITS, query_string);
245
246 let response = self.make_authenticated_request(&url).await?;
247
248 if !response.status().is_success() {
249 let error_text = response
250 .text()
251 .await
252 .unwrap_or_else(|_| "Unknown error".to_string());
253 return Err(HttpError::RequestFailed(format!(
254 "Get deposits failed: {}",
255 error_text
256 )));
257 }
258
259 let api_response: ApiResponse<DepositsResponse> = response
260 .json()
261 .await
262 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
263
264 if let Some(error) = api_response.error {
265 return Err(HttpError::RequestFailed(format!(
266 "API error: {} - {}",
267 error.code, error.message
268 )));
269 }
270
271 api_response
272 .result
273 .ok_or_else(|| HttpError::InvalidResponse("No deposits data in response".to_string()))
274 }
275
276 pub async fn get_withdrawals(
296 &self,
297 currency: &str,
298 count: Option<u32>,
299 offset: Option<u32>,
300 ) -> Result<WithdrawalsResponse, HttpError> {
301 let mut query_params = vec![("currency".to_string(), currency.to_string())];
302
303 if let Some(count) = count {
304 query_params.push(("count".to_string(), count.to_string()));
305 }
306
307 if let Some(offset) = offset {
308 query_params.push(("offset".to_string(), offset.to_string()));
309 }
310
311 let query_string = query_params
312 .iter()
313 .map(|(k, v)| format!("{}={}", k, urlencoding::encode(v)))
314 .collect::<Vec<_>>()
315 .join("&");
316
317 let url = format!("{}{}?{}", self.base_url(), GET_WITHDRAWALS, query_string);
318
319 let response = self.make_authenticated_request(&url).await?;
320
321 if !response.status().is_success() {
322 let error_text = response
323 .text()
324 .await
325 .unwrap_or_else(|_| "Unknown error".to_string());
326 return Err(HttpError::RequestFailed(format!(
327 "Get withdrawals failed: {}",
328 error_text
329 )));
330 }
331
332 let api_response: ApiResponse<WithdrawalsResponse> = response
333 .json()
334 .await
335 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
336
337 if let Some(error) = api_response.error {
338 return Err(HttpError::RequestFailed(format!(
339 "API error: {} - {}",
340 error.code, error.message
341 )));
342 }
343
344 api_response.result.ok_or_else(|| {
345 HttpError::InvalidResponse("No withdrawals data in response".to_string())
346 })
347 }
348
349 pub async fn submit_transfer_to_subaccount(
369 &self,
370 currency: &str,
371 amount: f64,
372 destination: u64,
373 ) -> Result<TransferResultResponse, HttpError> {
374 let query_params = [
375 ("currency".to_string(), currency.to_string()),
376 ("amount".to_string(), amount.to_string()),
377 ("destination".to_string(), destination.to_string()),
378 ];
379
380 let query_string = query_params
381 .iter()
382 .map(|(k, v)| format!("{}={}", k, urlencoding::encode(v)))
383 .collect::<Vec<_>>()
384 .join("&");
385
386 let url = format!(
387 "{}{}?{}",
388 self.base_url(),
389 SUBMIT_TRANSFER_TO_SUBACCOUNT,
390 query_string
391 );
392
393 let response = self.make_authenticated_request(&url).await?;
394
395 if !response.status().is_success() {
396 let error_text = response
397 .text()
398 .await
399 .unwrap_or_else(|_| "Unknown error".to_string());
400 return Err(HttpError::RequestFailed(format!(
401 "Submit transfer to subaccount failed: {}",
402 error_text
403 )));
404 }
405
406 let api_response: ApiResponse<TransferResultResponse> = response
407 .json()
408 .await
409 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
410
411 if let Some(error) = api_response.error {
412 return Err(HttpError::RequestFailed(format!(
413 "API error: {} - {}",
414 error.code, error.message
415 )));
416 }
417
418 api_response
419 .result
420 .ok_or_else(|| HttpError::InvalidResponse("No transfer result in response".to_string()))
421 }
422
423 pub async fn submit_transfer_to_user(
443 &self,
444 currency: &str,
445 amount: f64,
446 destination: &str,
447 ) -> Result<TransferResultResponse, HttpError> {
448 let query_params = [
449 ("currency".to_string(), currency.to_string()),
450 ("amount".to_string(), amount.to_string()),
451 ("destination".to_string(), destination.to_string()),
452 ];
453
454 let query_string = query_params
455 .iter()
456 .map(|(k, v)| format!("{}={}", k, urlencoding::encode(v)))
457 .collect::<Vec<_>>()
458 .join("&");
459
460 let url = format!(
461 "{}{}?{}",
462 self.base_url(),
463 SUBMIT_TRANSFER_TO_USER,
464 query_string
465 );
466
467 let response = self.make_authenticated_request(&url).await?;
468
469 if !response.status().is_success() {
470 let error_text = response
471 .text()
472 .await
473 .unwrap_or_else(|_| "Unknown error".to_string());
474 return Err(HttpError::RequestFailed(format!(
475 "Submit transfer to user failed: {}",
476 error_text
477 )));
478 }
479
480 let api_response: ApiResponse<TransferResultResponse> = response
481 .json()
482 .await
483 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
484
485 if let Some(error) = api_response.error {
486 return Err(HttpError::RequestFailed(format!(
487 "API error: {} - {}",
488 error.code, error.message
489 )));
490 }
491
492 api_response
493 .result
494 .ok_or_else(|| HttpError::InvalidResponse("No transfer result in response".to_string()))
495 }
496
497 pub async fn buy_order(&self, request: OrderRequest) -> Result<OrderResponse, HttpError> {
506 let mut query_params = vec![
507 ("instrument_name".to_string(), request.instrument_name),
508 (
509 "amount".to_string(),
510 request
511 .amount
512 .map_or_else(|| "0".to_string(), |a| a.to_string()),
513 ),
514 ];
515
516 if let Some(order_type) = request.type_ {
517 query_params.push(("type".to_string(), order_type.as_str().to_string()));
518 }
519
520 if let Some(price) = request.price {
521 query_params.push(("price".to_string(), price.to_string()));
522 }
523
524 if let Some(label) = request.label {
525 query_params.push(("label".to_string(), label));
526 }
527
528 if let Some(time_in_force) = request.time_in_force {
529 query_params.push((
530 "time_in_force".to_string(),
531 time_in_force.as_str().to_string(),
532 ));
533 }
534
535 if let Some(post_only) = request.post_only
536 && post_only
537 {
538 query_params.push(("post_only".to_string(), "true".to_string()));
539 }
540
541 if let Some(reduce_only) = request.reduce_only
542 && reduce_only
543 {
544 query_params.push(("reduce_only".to_string(), "true".to_string()));
545 }
546
547 if let Some(trigger_price) = request.trigger_price {
548 query_params.push(("trigger_price".to_string(), trigger_price.to_string()));
549 }
550
551 if let Some(trigger) = request.trigger {
552 let trigger_str = match trigger {
553 Trigger::IndexPrice => "index_price",
554 Trigger::MarkPrice => "mark_price",
555 Trigger::LastPrice => "last_price",
556 };
557 query_params.push(("trigger".to_string(), trigger_str.to_string()));
558 }
559
560 let query_string = query_params
561 .iter()
562 .map(|(k, v)| format!("{}={}", k, urlencoding::encode(v)))
563 .collect::<Vec<_>>()
564 .join("&");
565
566 let url = format!("{}{}?{}", self.base_url(), BUY, query_string);
567
568 let response = self.make_authenticated_request(&url).await?;
569
570 if !response.status().is_success() {
571 let error_text = response
572 .text()
573 .await
574 .unwrap_or_else(|_| "Unknown error".to_string());
575 return Err(HttpError::RequestFailed(format!(
576 "Buy order failed: {}",
577 error_text
578 )));
579 }
580
581 let response_text = response
583 .text()
584 .await
585 .map_err(|e| HttpError::NetworkError(e.to_string()))?;
586
587 tracing::debug!("Raw API response: {}", response_text);
588
589 let api_response: ApiResponse<OrderResponse> = serde_json::from_str(&response_text)
590 .map_err(|e| {
591 HttpError::InvalidResponse(format!(
592 "Failed to parse JSON: {} - Raw response: {}",
593 e, response_text
594 ))
595 })?;
596
597 if let Some(error) = api_response.error {
598 return Err(HttpError::RequestFailed(format!(
599 "API error: {} - {}",
600 error.code, error.message
601 )));
602 }
603
604 api_response
605 .result
606 .ok_or_else(|| HttpError::InvalidResponse("No order data in response".to_string()))
607 }
608
609 pub async fn sell_order(&self, request: OrderRequest) -> Result<OrderResponse, HttpError> {
617 let mut query_params = vec![
618 ("instrument_name".to_string(), request.instrument_name),
619 ("amount".to_string(), request.amount.unwrap().to_string()),
620 ];
621
622 if let Some(order_type) = request.type_ {
623 query_params.push(("type".to_string(), order_type.as_str().to_string()));
624 }
625
626 if let Some(price) = request.price {
627 query_params.push(("price".to_string(), price.to_string()));
628 }
629
630 if let Some(label) = request.label {
631 query_params.push(("label".to_string(), label));
632 }
633
634 if let Some(time_in_force) = request.time_in_force {
635 query_params.push((
636 "time_in_force".to_string(),
637 time_in_force.as_str().to_string(),
638 ));
639 }
640
641 if let Some(post_only) = request.post_only
642 && post_only
643 {
644 query_params.push(("post_only".to_string(), "true".to_string()));
645 }
646
647 if let Some(reduce_only) = request.reduce_only
648 && reduce_only
649 {
650 query_params.push(("reduce_only".to_string(), "true".to_string()));
651 }
652
653 if let Some(trigger_price) = request.trigger_price {
654 query_params.push(("trigger_price".to_string(), trigger_price.to_string()));
655 }
656
657 if let Some(trigger) = request.trigger {
658 let trigger_str = match trigger {
659 Trigger::IndexPrice => "index_price",
660 Trigger::MarkPrice => "mark_price",
661 Trigger::LastPrice => "last_price",
662 };
663 query_params.push(("trigger".to_string(), trigger_str.to_string()));
664 }
665
666 let query_string = query_params
667 .iter()
668 .map(|(k, v)| format!("{}={}", k, urlencoding::encode(v)))
669 .collect::<Vec<_>>()
670 .join("&");
671
672 let url = format!("{}{}?{}", self.base_url(), SELL, query_string);
673
674 let response = self.make_authenticated_request(&url).await?;
675
676 if !response.status().is_success() {
677 let error_text = response
678 .text()
679 .await
680 .unwrap_or_else(|_| "Unknown error".to_string());
681 return Err(HttpError::RequestFailed(format!(
682 "Sell order failed: {}",
683 error_text
684 )));
685 }
686
687 let api_response: ApiResponse<OrderResponse> = response
688 .json()
689 .await
690 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
691
692 if let Some(error) = api_response.error {
693 return Err(HttpError::RequestFailed(format!(
694 "API error: {} - {}",
695 error.code, error.message
696 )));
697 }
698
699 api_response
700 .result
701 .ok_or_else(|| HttpError::InvalidResponse("No order data in response".to_string()))
702 }
703
704 pub async fn cancel_order(&self, order_id: &str) -> Result<OrderInfoResponse, HttpError> {
713 let url = format!(
714 "{}{}?order_id={}",
715 self.base_url(),
716 CANCEL,
717 urlencoding::encode(order_id)
718 );
719
720 let response = self.make_authenticated_request(&url).await?;
721
722 if !response.status().is_success() {
723 let error_text = response
724 .text()
725 .await
726 .unwrap_or_else(|_| "Unknown error".to_string());
727 return Err(HttpError::RequestFailed(format!(
728 "Cancel order failed: {}",
729 error_text
730 )));
731 }
732
733 let api_response: ApiResponse<OrderInfoResponse> = response
734 .json()
735 .await
736 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
737
738 if let Some(error) = api_response.error {
739 return Err(HttpError::RequestFailed(format!(
740 "API error: {} - {}",
741 error.code, error.message
742 )));
743 }
744
745 api_response
746 .result
747 .ok_or_else(|| HttpError::InvalidResponse("No order data in response".to_string()))
748 }
749
750 pub async fn cancel_all(&self) -> Result<u32, HttpError> {
758 let url = format!("{}{}", self.base_url(), CANCEL_ALL);
759
760 let response = self.make_authenticated_request(&url).await?;
761
762 if !response.status().is_success() {
763 let error_text = response
764 .text()
765 .await
766 .unwrap_or_else(|_| "Unknown error".to_string());
767 return Err(HttpError::RequestFailed(format!(
768 "Cancel all orders failed: {}",
769 error_text
770 )));
771 }
772
773 let api_response: ApiResponse<u32> = response
774 .json()
775 .await
776 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
777
778 if let Some(error) = api_response.error {
779 return Err(HttpError::RequestFailed(format!(
780 "API error: {} - {}",
781 error.code, error.message
782 )));
783 }
784
785 api_response
786 .result
787 .ok_or_else(|| HttpError::InvalidResponse("No result in response".to_string()))
788 }
789
790 pub async fn cancel_all_by_currency(&self, currency: &str) -> Result<u32, HttpError> {
802 let url = format!(
803 "{}{}?currency={}",
804 self.base_url(),
805 CANCEL_ALL_BY_CURRENCY,
806 urlencoding::encode(currency)
807 );
808
809 let response = self.make_authenticated_request(&url).await?;
810
811 if !response.status().is_success() {
812 let error_text = response
813 .text()
814 .await
815 .unwrap_or_else(|_| "Unknown error".to_string());
816 return Err(HttpError::RequestFailed(format!(
817 "Cancel all orders by currency failed: {}",
818 error_text
819 )));
820 }
821
822 let api_response: ApiResponse<u32> = response
823 .json()
824 .await
825 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
826
827 if let Some(error) = api_response.error {
828 return Err(HttpError::RequestFailed(format!(
829 "API error: {} - {}",
830 error.code, error.message
831 )));
832 }
833
834 api_response
835 .result
836 .ok_or_else(|| HttpError::InvalidResponse("No result in response".to_string()))
837 }
838
839 pub async fn cancel_all_by_currency_pair(&self, currency_pair: &str) -> Result<u32, HttpError> {
851 let url = format!(
852 "{}{}?currency_pair={}",
853 self.base_url(),
854 CANCEL_ALL_BY_CURRENCY_PAIR,
855 urlencoding::encode(currency_pair)
856 );
857
858 let response = self.make_authenticated_request(&url).await?;
859
860 if !response.status().is_success() {
861 let error_text = response
862 .text()
863 .await
864 .unwrap_or_else(|_| "Unknown error".to_string());
865 return Err(HttpError::RequestFailed(format!(
866 "Cancel all orders by currency pair failed: {}",
867 error_text
868 )));
869 }
870
871 let api_response: ApiResponse<u32> = response
872 .json()
873 .await
874 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
875
876 if let Some(error) = api_response.error {
877 return Err(HttpError::RequestFailed(format!(
878 "API error: {} - {}",
879 error.code, error.message
880 )));
881 }
882
883 api_response
884 .result
885 .ok_or_else(|| HttpError::InvalidResponse("No result in response".to_string()))
886 }
887
888 pub async fn cancel_all_by_instrument(&self, instrument_name: &str) -> Result<u32, HttpError> {
900 let url = format!(
901 "{}{}?instrument_name={}",
902 self.base_url(),
903 CANCEL_ALL_BY_INSTRUMENT,
904 urlencoding::encode(instrument_name)
905 );
906
907 let response = self.make_authenticated_request(&url).await?;
908
909 if !response.status().is_success() {
910 let error_text = response
911 .text()
912 .await
913 .unwrap_or_else(|_| "Unknown error".to_string());
914 return Err(HttpError::RequestFailed(format!(
915 "Cancel all orders by instrument failed: {}",
916 error_text
917 )));
918 }
919
920 let api_response: ApiResponse<u32> = response
921 .json()
922 .await
923 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
924
925 if let Some(error) = api_response.error {
926 return Err(HttpError::RequestFailed(format!(
927 "API error: {} - {}",
928 error.code, error.message
929 )));
930 }
931
932 api_response
933 .result
934 .ok_or_else(|| HttpError::InvalidResponse("No result in response".to_string()))
935 }
936
937 pub async fn cancel_all_by_kind_or_type(
950 &self,
951 kind: Option<&str>,
952 order_type: Option<&str>,
953 ) -> Result<u32, HttpError> {
954 let mut query_params = Vec::new();
955
956 if let Some(kind) = kind {
957 query_params.push(("kind".to_string(), kind.to_string()));
958 }
959
960 if let Some(order_type) = order_type {
961 query_params.push(("type".to_string(), order_type.to_string()));
962 }
963
964 let query_string = if query_params.is_empty() {
965 String::new()
966 } else {
967 "?".to_string()
968 + &query_params
969 .iter()
970 .map(|(k, v)| format!("{}={}", k, urlencoding::encode(v)))
971 .collect::<Vec<_>>()
972 .join("&")
973 };
974
975 let url = format!(
976 "{}{}{}",
977 self.base_url(),
978 CANCEL_ALL_BY_KIND_OR_TYPE,
979 query_string
980 );
981
982 let response = self.make_authenticated_request(&url).await?;
983
984 if !response.status().is_success() {
985 let error_text = response
986 .text()
987 .await
988 .unwrap_or_else(|_| "Unknown error".to_string());
989 return Err(HttpError::RequestFailed(format!(
990 "Cancel all orders by kind or type failed: {}",
991 error_text
992 )));
993 }
994
995 let api_response: ApiResponse<u32> = response
996 .json()
997 .await
998 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
999
1000 if let Some(error) = api_response.error {
1001 return Err(HttpError::RequestFailed(format!(
1002 "API error: {} - {}",
1003 error.code, error.message
1004 )));
1005 }
1006
1007 api_response
1008 .result
1009 .ok_or_else(|| HttpError::InvalidResponse("No result in response".to_string()))
1010 }
1011
1012 pub async fn cancel_by_label(&self, label: &str) -> Result<u32, HttpError> {
1024 let url = format!(
1025 "{}{}?label={}",
1026 self.base_url(),
1027 CANCEL_BY_LABEL,
1028 urlencoding::encode(label)
1029 );
1030
1031 let response = self.make_authenticated_request(&url).await?;
1032
1033 if !response.status().is_success() {
1034 let error_text = response
1035 .text()
1036 .await
1037 .unwrap_or_else(|_| "Unknown error".to_string());
1038 return Err(HttpError::RequestFailed(format!(
1039 "Cancel orders by label failed: {}",
1040 error_text
1041 )));
1042 }
1043
1044 let api_response: ApiResponse<u32> = response
1045 .json()
1046 .await
1047 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
1048
1049 if let Some(error) = api_response.error {
1050 return Err(HttpError::RequestFailed(format!(
1051 "API error: {} - {}",
1052 error.code, error.message
1053 )));
1054 }
1055
1056 api_response
1057 .result
1058 .ok_or_else(|| HttpError::InvalidResponse("No result in response".to_string()))
1059 }
1060
1061 pub async fn get_account_summary(
1071 &self,
1072 currency: &str,
1073 extended: Option<bool>,
1074 ) -> Result<AccountSummaryResponse, HttpError> {
1075 let mut query_params = vec![("currency".to_string(), currency.to_string())];
1076
1077 if let Some(extended) = extended {
1078 query_params.push(("extended".to_string(), extended.to_string()));
1079 }
1080
1081 let query_string = query_params
1082 .iter()
1083 .map(|(k, v)| format!("{}={}", k, urlencoding::encode(v)))
1084 .collect::<Vec<_>>()
1085 .join("&");
1086
1087 let url = format!(
1088 "{}{}?{}",
1089 self.base_url(),
1090 GET_ACCOUNT_SUMMARY,
1091 query_string
1092 );
1093
1094 let response = self.make_authenticated_request(&url).await?;
1095
1096 if !response.status().is_success() {
1097 let error_text = response
1098 .text()
1099 .await
1100 .unwrap_or_else(|_| "Unknown error".to_string());
1101 return Err(HttpError::RequestFailed(format!(
1102 "Get account summary failed: {}",
1103 error_text
1104 )));
1105 }
1106
1107 let api_response: ApiResponse<AccountSummaryResponse> = response
1108 .json()
1109 .await
1110 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
1111
1112 if let Some(error) = api_response.error {
1113 return Err(HttpError::RequestFailed(format!(
1114 "API error: {} - {}",
1115 error.code, error.message
1116 )));
1117 }
1118
1119 api_response.result.ok_or_else(|| {
1120 HttpError::InvalidResponse("No account summary data in response".to_string())
1121 })
1122 }
1123
1124 pub async fn get_positions(
1144 &self,
1145 currency: Option<&str>,
1146 kind: Option<&str>,
1147 subaccount_id: Option<i32>,
1148 ) -> Result<Vec<Position>, HttpError> {
1149 let mut query_params = Vec::new();
1150
1151 if let Some(currency) = currency {
1152 query_params.push(("currency".to_string(), currency.to_string()));
1153 }
1154
1155 if let Some(kind) = kind {
1156 query_params.push(("kind".to_string(), kind.to_string()));
1157 }
1158
1159 if let Some(subaccount_id) = subaccount_id {
1160 query_params.push(("subaccount_id".to_string(), subaccount_id.to_string()));
1161 }
1162
1163 let query_string = if query_params.is_empty() {
1164 String::new()
1165 } else {
1166 "?".to_string()
1167 + &query_params
1168 .iter()
1169 .map(|(k, v)| format!("{}={}", k, urlencoding::encode(v)))
1170 .collect::<Vec<_>>()
1171 .join("&")
1172 };
1173
1174 let url = format!("{}{}{}", self.base_url(), GET_POSITIONS, query_string);
1175
1176 let response = self.make_authenticated_request(&url).await?;
1177
1178 if !response.status().is_success() {
1179 let error_text = response
1180 .text()
1181 .await
1182 .unwrap_or_else(|_| "Unknown error".to_string());
1183 return Err(HttpError::RequestFailed(format!(
1184 "Get positions failed: {}",
1185 error_text
1186 )));
1187 }
1188
1189 let api_response: ApiResponse<Vec<Position>> = response
1190 .json()
1191 .await
1192 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
1193
1194 if let Some(error) = api_response.error {
1195 return Err(HttpError::RequestFailed(format!(
1196 "API error: {} - {}",
1197 error.code, error.message
1198 )));
1199 }
1200
1201 api_response
1202 .result
1203 .ok_or_else(|| HttpError::InvalidResponse("No positions data in response".to_string()))
1204 }
1205
1206 pub async fn get_position(&self, instrument_name: &str) -> Result<Vec<Position>, HttpError> {
1219 let query_string = format!("instrument_name={}", instrument_name);
1220 let url = format!("{}{}{}", self.base_url(), GET_POSITION, query_string);
1221 let response = self.make_authenticated_request(&url).await?;
1222
1223 if !response.status().is_success() {
1224 let error_text = response
1225 .text()
1226 .await
1227 .unwrap_or_else(|_| "Unknown error".to_string());
1228 return Err(HttpError::RequestFailed(format!(
1229 "Get positions failed: {}",
1230 error_text
1231 )));
1232 }
1233
1234 let api_response: ApiResponse<Vec<Position>> = response
1235 .json()
1236 .await
1237 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
1238
1239 if let Some(error) = api_response.error {
1240 return Err(HttpError::RequestFailed(format!(
1241 "API error: {} - {}",
1242 error.code, error.message
1243 )));
1244 }
1245
1246 api_response
1247 .result
1248 .ok_or_else(|| HttpError::InvalidResponse("No positions data in response".to_string()))
1249 }
1250
1251 pub async fn edit_order(&self, request: OrderRequest) -> Result<OrderResponse, HttpError> {
1260 let order_id = request.order_id.ok_or_else(|| {
1261 HttpError::RequestFailed("order_id is required for edit_order".to_string())
1262 })?;
1263 let mut query_params = vec![("order_id", order_id.as_str())];
1264
1265 let amount_str;
1266 if let Some(amount) = request.amount {
1267 amount_str = amount.to_string();
1268 query_params.push(("amount", amount_str.as_str()));
1269 }
1270
1271 let price_str;
1272 if let Some(price) = request.price {
1273 price_str = price.to_string();
1274 query_params.push(("price", price_str.as_str()));
1275 }
1276
1277 if let Some(post_only) = request.post_only
1278 && post_only
1279 {
1280 query_params.push(("post_only", "true"));
1281 }
1282
1283 if let Some(reduce_only) = request.reduce_only
1284 && reduce_only
1285 {
1286 query_params.push(("reduce_only", "true"));
1287 }
1288
1289 let query_string = query_params
1290 .iter()
1291 .map(|(k, v)| format!("{}={}", k, urlencoding::encode(v)))
1292 .collect::<Vec<_>>()
1293 .join("&");
1294
1295 let url = format!("{}{}?{}", self.base_url(), EDIT, query_string);
1296
1297 let response = self.make_authenticated_request(&url).await?;
1298
1299 if !response.status().is_success() {
1300 let error_text = response
1301 .text()
1302 .await
1303 .unwrap_or_else(|_| "Unknown error".to_string());
1304 return Err(HttpError::RequestFailed(format!(
1305 "Edit order failed: {}",
1306 error_text
1307 )));
1308 }
1309
1310 let api_response: ApiResponse<OrderResponse> = response
1311 .json()
1312 .await
1313 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
1314
1315 if let Some(error) = api_response.error {
1316 return Err(HttpError::RequestFailed(format!(
1317 "API error: {} - {}",
1318 error.code, error.message
1319 )));
1320 }
1321
1322 api_response
1323 .result
1324 .ok_or_else(|| HttpError::InvalidResponse("No order data in response".to_string()))
1325 }
1326
1327 pub async fn mass_quote(
1341 &self,
1342 _quotes: MassQuoteRequest,
1343 ) -> Result<MassQuoteResponse, HttpError> {
1344 Err(HttpError::ConfigError(
1345 "Mass quote endpoint is only available via WebSocket connections. \
1346 According to Deribit's technical specifications, private/mass_quote requires \
1347 WebSocket for real-time quote management, MMP group integration, and \
1348 Cancel-on-Disconnect functionality. Please use the deribit-websocket client \
1349 for mass quote operations."
1350 .to_string(),
1351 ))
1352 }
1353
1354 pub async fn get_user_trades_by_instrument(
1368 &self,
1369 instrument_name: &str,
1370 start_seq: Option<u64>,
1371 end_seq: Option<u64>,
1372 count: Option<u32>,
1373 include_old: Option<bool>,
1374 sorting: Option<&str>,
1375 ) -> Result<UserTradeWithPaginationResponse, HttpError> {
1376 let mut query_params = vec![("instrument_name".to_string(), instrument_name.to_string())];
1377
1378 if let Some(start_seq) = start_seq {
1379 query_params.push(("start_seq".to_string(), start_seq.to_string()));
1380 }
1381
1382 if let Some(end_seq) = end_seq {
1383 query_params.push(("end_seq".to_string(), end_seq.to_string()));
1384 }
1385
1386 if let Some(count) = count {
1387 query_params.push(("count".to_string(), count.to_string()));
1388 }
1389
1390 if let Some(include_old) = include_old {
1391 query_params.push(("include_old".to_string(), include_old.to_string()));
1392 }
1393
1394 if let Some(sorting) = sorting {
1395 query_params.push(("sorting".to_string(), sorting.to_string()));
1396 }
1397
1398 let query_string = query_params
1399 .iter()
1400 .map(|(k, v)| format!("{}={}", k, urlencoding::encode(v)))
1401 .collect::<Vec<_>>()
1402 .join("&");
1403
1404 let url = format!(
1405 "{}{}?{}",
1406 self.base_url(),
1407 GET_USER_TRADES_BY_INSTRUMENT,
1408 query_string
1409 );
1410
1411 let response = self.make_authenticated_request(&url).await?;
1412
1413 if !response.status().is_success() {
1414 let error_text = response
1415 .text()
1416 .await
1417 .unwrap_or_else(|_| "Unknown error".to_string());
1418 return Err(HttpError::RequestFailed(format!(
1419 "Get user trades by instrument failed: {}",
1420 error_text
1421 )));
1422 }
1423
1424 let response_text = response.text().await.map_err(|e| {
1426 HttpError::InvalidResponse(format!("Failed to read response text: {}", e))
1427 })?;
1428
1429 tracing::debug!(
1430 "Raw API response for get_user_trades_by_instrument: {}",
1431 response_text
1432 );
1433
1434 let api_response: ApiResponse<UserTradeWithPaginationResponse> =
1436 serde_json::from_str(&response_text).map_err(|e| {
1437 HttpError::InvalidResponse(format!(
1438 "error decoding response body: {} - Raw response: {}",
1439 e, response_text
1440 ))
1441 })?;
1442
1443 if let Some(error) = api_response.error {
1444 return Err(HttpError::RequestFailed(format!(
1445 "API error: {} - {}",
1446 error.code, error.message
1447 )));
1448 }
1449
1450 api_response.result.ok_or_else(|| {
1451 HttpError::InvalidResponse("No user trades data in response".to_string())
1452 })
1453 }
1454
1455 pub async fn cancel_quotes(&self, cancel_type: Option<&str>) -> Result<u32, HttpError> {
1464 let mut url = format!("{}{}", self.base_url(), CANCEL_QUOTES);
1465
1466 if let Some(cancel_type) = cancel_type {
1467 url.push_str(&format!(
1468 "?cancel_type={}",
1469 urlencoding::encode(cancel_type)
1470 ));
1471 } else {
1472 url.push_str("?cancel_type=all");
1473 }
1474
1475 let response = self.make_authenticated_request(&url).await?;
1476
1477 if !response.status().is_success() {
1478 let error_text = response
1479 .text()
1480 .await
1481 .unwrap_or_else(|_| "Unknown error".to_string());
1482 return Err(HttpError::RequestFailed(format!(
1483 "Cancel quotes failed: {}",
1484 error_text
1485 )));
1486 }
1487
1488 let api_response: ApiResponse<u32> = response
1489 .json()
1490 .await
1491 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
1492
1493 if let Some(error) = api_response.error {
1494 return Err(HttpError::RequestFailed(format!(
1495 "API error: {} - {}",
1496 error.code, error.message
1497 )));
1498 }
1499
1500 api_response
1501 .result
1502 .ok_or_else(|| HttpError::InvalidResponse("No cancel result in response".to_string()))
1503 }
1504
1505 pub async fn get_open_orders(
1515 &self,
1516 kind: Option<&str>,
1517 order_type: Option<&str>,
1518 ) -> Result<Vec<OrderInfoResponse>, HttpError> {
1519 let mut query_params = Vec::new();
1520
1521 if let Some(kind) = kind {
1522 query_params.push(("kind".to_string(), kind.to_string()));
1523 }
1524
1525 if let Some(order_type) = order_type {
1526 query_params.push(("type".to_string(), order_type.to_string()));
1527 }
1528
1529 let query_string = if query_params.is_empty() {
1530 String::new()
1531 } else {
1532 "?".to_string()
1533 + &query_params
1534 .iter()
1535 .map(|(k, v)| format!("{}={}", k, urlencoding::encode(v)))
1536 .collect::<Vec<_>>()
1537 .join("&")
1538 };
1539
1540 let url = format!("{}{}{}", self.base_url(), GET_OPEN_ORDERS, query_string);
1541
1542 let response = self.make_authenticated_request(&url).await?;
1543
1544 if !response.status().is_success() {
1545 let error_text = response
1546 .text()
1547 .await
1548 .unwrap_or_else(|_| "Unknown error".to_string());
1549 return Err(HttpError::RequestFailed(format!(
1550 "Get open orders failed: {}",
1551 error_text
1552 )));
1553 }
1554
1555 let api_response: ApiResponse<Vec<OrderInfoResponse>> = response
1556 .json()
1557 .await
1558 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
1559
1560 if let Some(error) = api_response.error {
1561 return Err(HttpError::RequestFailed(format!(
1562 "API error: {} - {}",
1563 error.code, error.message
1564 )));
1565 }
1566
1567 api_response
1568 .result
1569 .ok_or_else(|| HttpError::InvalidResponse("No orders data in response".to_string()))
1570 }
1571
1572 pub async fn get_open_orders_by_label(
1582 &self,
1583 label: &str,
1584 currency: &str,
1585 ) -> Result<Vec<OrderInfoResponse>, HttpError> {
1586 let url = format!(
1587 "{}{}?label={}¤cy={}",
1588 self.base_url(),
1589 GET_OPEN_ORDERS_BY_LABEL,
1590 urlencoding::encode(label),
1591 urlencoding::encode(currency)
1592 );
1593
1594 let response = self.make_authenticated_request(&url).await?;
1595
1596 if !response.status().is_success() {
1597 let error_text = response
1598 .text()
1599 .await
1600 .unwrap_or_else(|_| "Unknown error".to_string());
1601 return Err(HttpError::RequestFailed(format!(
1602 "Get open orders by label failed: {}",
1603 error_text
1604 )));
1605 }
1606
1607 let api_response: ApiResponse<Vec<OrderInfoResponse>> = response
1608 .json()
1609 .await
1610 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
1611
1612 if let Some(error) = api_response.error {
1613 return Err(HttpError::RequestFailed(format!(
1614 "API error: {} - {}",
1615 error.code, error.message
1616 )));
1617 }
1618
1619 api_response
1620 .result
1621 .ok_or_else(|| HttpError::InvalidResponse("No orders data in response".to_string()))
1622 }
1623
1624 pub async fn get_order_state(&self, order_id: &str) -> Result<OrderInfoResponse, HttpError> {
1633 let url = format!(
1634 "{}{}?order_id={}",
1635 self.base_url(),
1636 GET_ORDER_STATE,
1637 urlencoding::encode(order_id)
1638 );
1639
1640 let response = self.make_authenticated_request(&url).await?;
1641
1642 if !response.status().is_success() {
1643 let error_text = response
1644 .text()
1645 .await
1646 .unwrap_or_else(|_| "Unknown error".to_string());
1647 return Err(HttpError::RequestFailed(format!(
1648 "Get order state failed: {}",
1649 error_text
1650 )));
1651 }
1652
1653 let api_response: ApiResponse<OrderInfoResponse> = response
1654 .json()
1655 .await
1656 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
1657
1658 if let Some(error) = api_response.error {
1659 return Err(HttpError::RequestFailed(format!(
1660 "API error: {} - {}",
1661 error.code, error.message
1662 )));
1663 }
1664
1665 api_response
1666 .result
1667 .ok_or_else(|| HttpError::InvalidResponse("No order data in response".to_string()))
1668 }
1669
1670 pub async fn get_open_orders_by_currency(
1681 &self,
1682 currency: &str,
1683 kind: Option<&str>,
1684 order_type: Option<&str>,
1685 ) -> Result<Vec<OrderInfoResponse>, HttpError> {
1686 let mut query_params = vec![("currency".to_string(), currency.to_string())];
1687
1688 if let Some(kind) = kind {
1689 query_params.push(("kind".to_string(), kind.to_string()));
1690 }
1691
1692 if let Some(order_type) = order_type {
1693 query_params.push(("type".to_string(), order_type.to_string()));
1694 }
1695
1696 let query_string = query_params
1697 .iter()
1698 .map(|(k, v)| format!("{}={}", k, urlencoding::encode(v)))
1699 .collect::<Vec<_>>()
1700 .join("&");
1701
1702 let url = format!(
1703 "{}{}?{}",
1704 self.base_url(),
1705 GET_OPEN_ORDERS_BY_CURRENCY,
1706 query_string
1707 );
1708
1709 let response = self.make_authenticated_request(&url).await?;
1710
1711 if !response.status().is_success() {
1712 let error_text = response
1713 .text()
1714 .await
1715 .unwrap_or_else(|_| "Unknown error".to_string());
1716 return Err(HttpError::RequestFailed(format!(
1717 "Get open orders by currency failed: {}",
1718 error_text
1719 )));
1720 }
1721
1722 let api_response: ApiResponse<Vec<OrderInfoResponse>> = response
1723 .json()
1724 .await
1725 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
1726
1727 if let Some(error) = api_response.error {
1728 return Err(HttpError::RequestFailed(format!(
1729 "API error: {} - {}",
1730 error.code, error.message
1731 )));
1732 }
1733
1734 api_response
1735 .result
1736 .ok_or_else(|| HttpError::InvalidResponse("No orders data in response".to_string()))
1737 }
1738
1739 pub async fn get_open_orders_by_instrument(
1749 &self,
1750 instrument_name: &str,
1751 order_type: Option<&str>,
1752 ) -> Result<Vec<OrderInfoResponse>, HttpError> {
1753 let mut query_params = vec![("instrument_name".to_string(), instrument_name.to_string())];
1754
1755 if let Some(order_type) = order_type {
1756 query_params.push(("type".to_string(), order_type.to_string()));
1757 }
1758
1759 let query_string = query_params
1760 .iter()
1761 .map(|(k, v)| format!("{}={}", k, urlencoding::encode(v)))
1762 .collect::<Vec<_>>()
1763 .join("&");
1764
1765 let url = format!(
1766 "{}{}?{}",
1767 self.base_url(),
1768 GET_OPEN_ORDERS_BY_INSTRUMENT,
1769 query_string
1770 );
1771
1772 let response = self.make_authenticated_request(&url).await?;
1773
1774 if !response.status().is_success() {
1775 let error_text = response
1776 .text()
1777 .await
1778 .unwrap_or_else(|_| "Unknown error".to_string());
1779 return Err(HttpError::RequestFailed(format!(
1780 "Get open orders by instrument failed: {}",
1781 error_text
1782 )));
1783 }
1784
1785 let api_response: ApiResponse<Vec<OrderInfoResponse>> = response
1786 .json()
1787 .await
1788 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
1789
1790 if let Some(error) = api_response.error {
1791 return Err(HttpError::RequestFailed(format!(
1792 "API error: {} - {}",
1793 error.code, error.message
1794 )));
1795 }
1796
1797 api_response
1798 .result
1799 .ok_or_else(|| HttpError::InvalidResponse("No orders data in response".to_string()))
1800 }
1801
1802 pub async fn get_order_history(
1814 &self,
1815 currency: &str,
1816 kind: Option<&str>,
1817 count: Option<u32>,
1818 offset: Option<u32>,
1819 ) -> Result<Vec<OrderInfoResponse>, HttpError> {
1820 let mut query_params = vec![("currency".to_string(), currency.to_string())];
1821
1822 if let Some(kind) = kind {
1823 query_params.push(("kind".to_string(), kind.to_string()));
1824 }
1825
1826 if let Some(count) = count {
1827 query_params.push(("count".to_string(), count.to_string()));
1828 }
1829
1830 if let Some(offset) = offset {
1831 query_params.push(("offset".to_string(), offset.to_string()));
1832 }
1833
1834 let query_string = query_params
1835 .iter()
1836 .map(|(k, v)| format!("{}={}", k, urlencoding::encode(v)))
1837 .collect::<Vec<_>>()
1838 .join("&");
1839
1840 let url = format!(
1841 "{}{}?{}",
1842 self.base_url(),
1843 GET_ORDER_HISTORY_BY_CURRENCY,
1844 query_string
1845 );
1846 let response = self.make_authenticated_request(&url).await?;
1847
1848 if !response.status().is_success() {
1849 let error_text = response
1850 .text()
1851 .await
1852 .unwrap_or_else(|_| "Unknown error".to_string());
1853 return Err(HttpError::RequestFailed(format!(
1854 "Get order history failed: {}",
1855 error_text
1856 )));
1857 }
1858
1859 let api_response: ApiResponse<Vec<OrderInfoResponse>> = response
1860 .json()
1861 .await
1862 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
1863
1864 if let Some(error) = api_response.error {
1865 return Err(HttpError::RequestFailed(format!(
1866 "API error: {} - {}",
1867 error.code, error.message
1868 )));
1869 }
1870
1871 api_response.result.ok_or_else(|| {
1872 HttpError::InvalidResponse("No order history data in response".to_string())
1873 })
1874 }
1875
1876 pub async fn get_order_history_by_currency(
1888 &self,
1889 currency: &str,
1890 kind: Option<&str>,
1891 count: Option<u32>,
1892 offset: Option<u32>,
1893 ) -> Result<Vec<OrderInfoResponse>, HttpError> {
1894 self.get_order_history(currency, kind, count, offset).await
1896 }
1897
1898 pub async fn get_order_history_by_instrument(
1909 &self,
1910 instrument_name: &str,
1911 count: Option<u32>,
1912 offset: Option<u32>,
1913 ) -> Result<Vec<OrderInfoResponse>, HttpError> {
1914 let mut query_params = vec![("instrument_name".to_string(), instrument_name.to_string())];
1915
1916 if let Some(count) = count {
1917 query_params.push(("count".to_string(), count.to_string()));
1918 }
1919
1920 if let Some(offset) = offset {
1921 query_params.push(("offset".to_string(), offset.to_string()));
1922 }
1923
1924 let query_string = query_params
1925 .iter()
1926 .map(|(k, v)| format!("{}={}", k, urlencoding::encode(v)))
1927 .collect::<Vec<_>>()
1928 .join("&");
1929
1930 let url = format!(
1931 "{}{}?{}",
1932 self.base_url(),
1933 GET_ORDER_HISTORY_BY_INSTRUMENT,
1934 query_string
1935 );
1936
1937 let response = self.make_authenticated_request(&url).await?;
1938
1939 if !response.status().is_success() {
1940 let error_text = response
1941 .text()
1942 .await
1943 .unwrap_or_else(|_| "Unknown error".to_string());
1944 return Err(HttpError::RequestFailed(format!(
1945 "Get order history by instrument failed: {}",
1946 error_text
1947 )));
1948 }
1949
1950 let api_response: ApiResponse<Vec<OrderInfoResponse>> = response
1951 .json()
1952 .await
1953 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
1954
1955 if let Some(error) = api_response.error {
1956 return Err(HttpError::RequestFailed(format!(
1957 "API error: {} - {}",
1958 error.code, error.message
1959 )));
1960 }
1961
1962 api_response.result.ok_or_else(|| {
1963 HttpError::InvalidResponse("No order history data in response".to_string())
1964 })
1965 }
1966
1967 #[allow(clippy::too_many_arguments)]
1987 pub async fn get_user_trades_by_currency(
1988 &self,
1989 request: TradesRequest,
1990 ) -> Result<UserTradeWithPaginationResponse, HttpError> {
1991 let mut query_params = vec![("currency".to_string(), request.currency.to_string())];
1992
1993 if let Some(kind) = request.kind {
1994 query_params.push(("kind".to_string(), kind.to_string()));
1995 }
1996
1997 if let Some(start_id) = request.start_id {
1998 query_params.push(("start_id".to_string(), start_id));
1999 }
2000
2001 if let Some(end_id) = request.end_id {
2002 query_params.push(("end_id".to_string(), end_id));
2003 }
2004
2005 if let Some(count) = request.count {
2006 query_params.push(("count".to_string(), count.to_string()));
2007 }
2008
2009 if let Some(start_timestamp) = request.start_timestamp {
2010 query_params.push(("start_timestamp".to_string(), start_timestamp.to_string()));
2011 }
2012
2013 if let Some(end_timestamp) = request.end_timestamp {
2014 query_params.push(("end_timestamp".to_string(), end_timestamp.to_string()));
2015 }
2016
2017 if let Some(sorting) = request.sorting {
2018 query_params.push(("sorting".to_string(), sorting.to_string()));
2019 }
2020
2021 if let Some(historical) = request.historical {
2022 query_params.push(("historical".to_string(), historical.to_string()));
2023 }
2024
2025 if let Some(subaccount_id) = request.subaccount_id {
2026 query_params.push(("subaccount_id".to_string(), subaccount_id.to_string()));
2027 }
2028
2029 let query_string = query_params
2030 .iter()
2031 .map(|(k, v)| format!("{}={}", k, urlencoding::encode(v)))
2032 .collect::<Vec<_>>()
2033 .join("&");
2034
2035 let url = format!(
2036 "{}{}?{}",
2037 self.base_url(),
2038 GET_USER_TRADES_BY_CURRENCY,
2039 query_string
2040 );
2041
2042 let response = self.make_authenticated_request(&url).await?;
2043
2044 if !response.status().is_success() {
2045 let error_text = response
2046 .text()
2047 .await
2048 .unwrap_or_else(|_| "Unknown error".to_string());
2049 return Err(HttpError::RequestFailed(format!(
2050 "Get user trades by currency failed: {}",
2051 error_text
2052 )));
2053 }
2054
2055 let response_text = response.text().await.map_err(|e| {
2057 HttpError::InvalidResponse(format!("Failed to read response text: {}", e))
2058 })?;
2059
2060 tracing::debug!(
2061 "Raw API response for get_user_trades_by_order: {}",
2062 response_text
2063 );
2064
2065 let api_response: ApiResponse<UserTradeWithPaginationResponse> =
2067 serde_json::from_str(&response_text).map_err(|e| {
2068 HttpError::InvalidResponse(format!(
2069 "error decoding response body: {} - Raw response: {}",
2070 e, response_text
2071 ))
2072 })?;
2073
2074 if let Some(error) = api_response.error {
2075 return Err(HttpError::RequestFailed(format!(
2076 "API error: {} - {}",
2077 error.code, error.message
2078 )));
2079 }
2080
2081 api_response.result.ok_or_else(|| {
2082 HttpError::InvalidResponse("No user trades data in response".to_string())
2083 })
2084 }
2085
2086 #[allow(clippy::too_many_arguments)]
2106 pub async fn get_user_trades_by_currency_and_time(
2107 &self,
2108 request: TradesRequest,
2109 ) -> Result<UserTradeWithPaginationResponse, HttpError> {
2110 let mut query_params = vec![("currency".to_string(), request.currency.to_string())];
2111
2112 if let Some(kind) = request.kind {
2113 query_params.push(("kind".to_string(), kind.to_string()));
2114 }
2115
2116 if let Some(start_id) = request.start_id {
2117 query_params.push(("start_id".to_string(), start_id));
2118 }
2119
2120 if let Some(end_id) = request.end_id {
2121 query_params.push(("end_id".to_string(), end_id));
2122 }
2123
2124 if let Some(count) = request.count {
2125 query_params.push(("count".to_string(), count.to_string()));
2126 }
2127
2128 if let Some(start_timestamp) = request.start_timestamp {
2129 query_params.push(("start_timestamp".to_string(), start_timestamp.to_string()));
2130 }
2131
2132 if let Some(end_timestamp) = request.end_timestamp {
2133 query_params.push(("end_timestamp".to_string(), end_timestamp.to_string()));
2134 }
2135
2136 if let Some(sorting) = request.sorting {
2137 query_params.push(("sorting".to_string(), sorting.to_string()));
2138 }
2139
2140 if let Some(historical) = request.historical {
2141 query_params.push(("historical".to_string(), historical.to_string()));
2142 }
2143
2144 if let Some(subaccount_id) = request.subaccount_id {
2145 query_params.push(("subaccount_id".to_string(), subaccount_id.to_string()));
2146 }
2147
2148 let query_string = query_params
2149 .iter()
2150 .map(|(k, v)| format!("{}={}", k, urlencoding::encode(v)))
2151 .collect::<Vec<_>>()
2152 .join("&");
2153
2154 let url = format!(
2155 "{}{}?{}",
2156 self.base_url(),
2157 GET_USER_TRADES_BY_CURRENCY_AND_TIME,
2158 query_string
2159 );
2160
2161 let response = self.make_authenticated_request(&url).await?;
2162
2163 if !response.status().is_success() {
2164 let error_text = response
2165 .text()
2166 .await
2167 .unwrap_or_else(|_| "Unknown error".to_string());
2168 return Err(HttpError::RequestFailed(format!(
2169 "Get user trades by currency and time failed: {}",
2170 error_text
2171 )));
2172 }
2173
2174 let response_text = response.text().await.map_err(|e| {
2176 HttpError::InvalidResponse(format!("Failed to read response text: {}", e))
2177 })?;
2178
2179 tracing::debug!(
2180 "Raw API response for get_user_trades_by_order: {}",
2181 response_text
2182 );
2183
2184 let api_response: ApiResponse<UserTradeWithPaginationResponse> =
2186 serde_json::from_str(&response_text).map_err(|e| {
2187 HttpError::InvalidResponse(format!(
2188 "error decoding response body: {} - Raw response: {}",
2189 e, response_text
2190 ))
2191 })?;
2192
2193 if let Some(error) = api_response.error {
2194 return Err(HttpError::RequestFailed(format!(
2195 "API error: {} - {}",
2196 error.code, error.message
2197 )));
2198 }
2199
2200 api_response.result.ok_or_else(|| {
2201 HttpError::InvalidResponse("No user trades data in response".to_string())
2202 })
2203 }
2204
2205 pub async fn get_user_trades_by_instrument_and_time(
2219 &self,
2220 instrument_name: &str,
2221 start_timestamp: u64,
2222 end_timestamp: u64,
2223 count: Option<u32>,
2224 include_old: Option<bool>,
2225 sorting: Option<&str>,
2226 ) -> Result<UserTradeWithPaginationResponse, HttpError> {
2227 let mut query_params = vec![
2228 ("instrument_name".to_string(), instrument_name.to_string()),
2229 ("start_timestamp".to_string(), start_timestamp.to_string()),
2230 ("end_timestamp".to_string(), end_timestamp.to_string()),
2231 ];
2232
2233 if let Some(count) = count {
2234 query_params.push(("count".to_string(), count.to_string()));
2235 }
2236
2237 if let Some(include_old) = include_old {
2238 query_params.push(("include_old".to_string(), include_old.to_string()));
2239 }
2240
2241 if let Some(sorting) = sorting {
2242 query_params.push(("sorting".to_string(), sorting.to_string()));
2243 }
2244
2245 let query_string = query_params
2246 .iter()
2247 .map(|(k, v)| format!("{}={}", k, urlencoding::encode(v)))
2248 .collect::<Vec<_>>()
2249 .join("&");
2250
2251 let url = format!(
2252 "{}{}?{}",
2253 self.base_url(),
2254 GET_USER_TRADES_BY_INSTRUMENT_AND_TIME,
2255 query_string
2256 );
2257
2258 let response = self.make_authenticated_request(&url).await?;
2259
2260 if !response.status().is_success() {
2261 let error_text = response
2262 .text()
2263 .await
2264 .unwrap_or_else(|_| "Unknown error".to_string());
2265 return Err(HttpError::RequestFailed(format!(
2266 "Get user trades by instrument and time failed: {}",
2267 error_text
2268 )));
2269 }
2270
2271 let response_text = response.text().await.map_err(|e| {
2273 HttpError::InvalidResponse(format!("Failed to read response text: {}", e))
2274 })?;
2275
2276 tracing::debug!(
2277 "Raw API response for get_user_trades_by_instrument_and_time: {}",
2278 response_text
2279 );
2280
2281 let api_response: ApiResponse<UserTradeWithPaginationResponse> =
2283 serde_json::from_str(&response_text).map_err(|e| {
2284 HttpError::InvalidResponse(format!(
2285 "error decoding response body: {} - Raw response: {}",
2286 e, response_text
2287 ))
2288 })?;
2289
2290 if let Some(error) = api_response.error {
2291 return Err(HttpError::RequestFailed(format!(
2292 "API error: {} - {}",
2293 error.code, error.message
2294 )));
2295 }
2296
2297 api_response.result.ok_or_else(|| {
2298 HttpError::InvalidResponse("No user trades data in response".to_string())
2299 })
2300 }
2301
2302 pub async fn get_user_trades_by_order(
2312 &self,
2313 order_id: &str,
2314 sorting: Option<&str>,
2315 historical: bool,
2316 ) -> Result<Vec<UserTradeResponseByOrder>, HttpError> {
2317 let mut query_params = vec![("order_id".to_string(), order_id.to_string())];
2318
2319 if let Some(sorting) = sorting {
2320 query_params.push(("sorting".to_string(), sorting.to_string()));
2321 }
2322 if historical {
2323 query_params.push(("historical".to_string(), historical.to_string()));
2324 }
2325
2326 let query_string = query_params
2327 .iter()
2328 .map(|(k, v)| format!("{}={}", k, urlencoding::encode(v)))
2329 .collect::<Vec<_>>()
2330 .join("&");
2331
2332 let url = format!(
2333 "{}{}?{}",
2334 self.base_url(),
2335 GET_USER_TRADES_BY_ORDER,
2336 query_string
2337 );
2338
2339 let response = self.make_authenticated_request(&url).await?;
2340
2341 if !response.status().is_success() {
2342 let error_text = response
2343 .text()
2344 .await
2345 .unwrap_or_else(|_| "Unknown error".to_string());
2346 return Err(HttpError::RequestFailed(format!(
2347 "Get user trades by order failed: {}",
2348 error_text
2349 )));
2350 }
2351
2352 let api_response: ApiResponse<Vec<UserTradeResponseByOrder>> = response
2353 .json()
2354 .await
2355 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
2356
2357 if let Some(error) = api_response.error {
2358 return Err(HttpError::RequestFailed(format!(
2359 "API error: {} - {}",
2360 error.code, error.message
2361 )));
2362 }
2363
2364 api_response.result.ok_or_else(|| {
2365 HttpError::InvalidResponse("No user trades data in response".to_string())
2366 })
2367 }
2368}