1use crate::DeribitHttpClient;
7use crate::constants::endpoints::*;
8use crate::error::HttpError;
9use crate::model::LastTradesResponse;
10use crate::model::book::{BookSummary, OrderBook};
11use crate::model::currency::CurrencyStruct;
12use crate::model::funding::{FundingChartData, FundingRateData};
13use crate::model::index::{IndexData, IndexPriceData};
14use crate::model::instrument::{Instrument, OptionType};
15use crate::model::order::OrderSide;
16use crate::model::other::{OptionInstrument, OptionInstrumentPair};
17use crate::model::response::api_response::ApiResponse;
18use crate::model::response::other::{
19 AprHistoryResponse, ContractSizeResponse, DeliveryPricesResponse, ExpirationsResponse,
20 SettlementsResponse, StatusResponse, TestResponse,
21};
22use crate::model::ticker::TickerData;
23use crate::model::trade::{Liquidity, Trade};
24use crate::model::tradingview::TradingViewChartData;
25use std::collections::HashMap;
26
27impl DeribitHttpClient {
29 pub async fn get_currencies(&self) -> Result<Vec<CurrencyStruct>, HttpError> {
49 let url = format!("{}{}", self.base_url(), GET_CURRENCIES);
50
51 let response = self
52 .http_client()
53 .get(url)
54 .send()
55 .await
56 .map_err(|e| HttpError::NetworkError(e.to_string()))?;
57
58 if !response.status().is_success() {
59 let error_text = response
60 .text()
61 .await
62 .unwrap_or_else(|_| "Unknown error".to_string());
63 return Err(HttpError::RequestFailed(format!(
64 "Get currencies failed: {}",
65 error_text
66 )));
67 }
68
69 let api_response: ApiResponse<Vec<CurrencyStruct>> = response
70 .json()
71 .await
72 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
73
74 if let Some(error) = api_response.error {
75 return Err(HttpError::RequestFailed(format!(
76 "API error: {} - {}",
77 error.code, error.message
78 )));
79 }
80
81 api_response
82 .result
83 .ok_or_else(|| HttpError::InvalidResponse("No currencies in response".to_string()))
84 }
85
86 pub async fn get_index(&self, currency: &str) -> Result<IndexData, HttpError> {
96 let url = format!("{}{}?currency={}", self.base_url(), GET_INDEX, currency);
97
98 let response = self
99 .http_client()
100 .get(&url)
101 .send()
102 .await
103 .map_err(|e| HttpError::NetworkError(e.to_string()))?;
104
105 if !response.status().is_success() {
106 let error_text = response
107 .text()
108 .await
109 .unwrap_or_else(|_| "Unknown error".to_string());
110 return Err(HttpError::RequestFailed(format!(
111 "Get index failed: {}",
112 error_text
113 )));
114 }
115
116 let api_response: ApiResponse<IndexData> = response
117 .json()
118 .await
119 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
120
121 if let Some(error) = api_response.error {
122 return Err(HttpError::RequestFailed(format!(
123 "API error: {} - {}",
124 error.code, error.message
125 )));
126 }
127
128 api_response
129 .result
130 .ok_or_else(|| HttpError::InvalidResponse("No index data in response".to_string()))
131 }
132
133 pub async fn get_index_price(&self, index_name: &str) -> Result<IndexPriceData, HttpError> {
155 let url = format!(
156 "{}{}?index_name={}",
157 self.base_url(),
158 GET_INDEX_PRICE,
159 index_name
160 );
161
162 let response = self
163 .http_client()
164 .get(&url)
165 .send()
166 .await
167 .map_err(|e| HttpError::NetworkError(e.to_string()))?;
168
169 if !response.status().is_success() {
170 let error_text = response
171 .text()
172 .await
173 .unwrap_or_else(|_| "Unknown error".to_string());
174 return Err(HttpError::RequestFailed(format!(
175 "Get index price failed: {}",
176 error_text
177 )));
178 }
179
180 let api_response: ApiResponse<IndexPriceData> = response
181 .json()
182 .await
183 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
184
185 if let Some(error) = api_response.error {
186 return Err(HttpError::RequestFailed(format!(
187 "API error: {} - {}",
188 error.code, error.message
189 )));
190 }
191
192 api_response.result.ok_or_else(|| {
193 HttpError::InvalidResponse("No index price data in response".to_string())
194 })
195 }
196
197 pub async fn get_index_price_names(&self) -> Result<Vec<String>, HttpError> {
217 let url = format!("{}{}", self.base_url(), GET_INDEX_PRICE_NAMES);
218
219 let response = self
220 .http_client()
221 .get(&url)
222 .send()
223 .await
224 .map_err(|e| HttpError::NetworkError(e.to_string()))?;
225
226 if !response.status().is_success() {
227 let error_text = response
228 .text()
229 .await
230 .unwrap_or_else(|_| "Unknown error".to_string());
231 return Err(HttpError::RequestFailed(format!(
232 "Get index price names failed: {}",
233 error_text
234 )));
235 }
236
237 let api_response: ApiResponse<Vec<String>> = response
238 .json()
239 .await
240 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
241
242 if let Some(error) = api_response.error {
243 return Err(HttpError::RequestFailed(format!(
244 "API error: {} - {}",
245 error.code, error.message
246 )));
247 }
248
249 api_response.result.ok_or_else(|| {
250 HttpError::InvalidResponse("No index price names in response".to_string())
251 })
252 }
253
254 pub async fn get_book_summary_by_currency(
280 &self,
281 currency: &str,
282 kind: Option<&str>,
283 ) -> Result<Vec<BookSummary>, HttpError> {
284 let mut url = format!(
285 "{}{}?currency={}",
286 self.base_url(),
287 GET_BOOK_SUMMARY_BY_CURRENCY,
288 currency
289 );
290
291 if let Some(kind) = kind {
292 url.push_str(&format!("&kind={}", kind));
293 }
294
295 let response = self
296 .http_client()
297 .get(&url)
298 .send()
299 .await
300 .map_err(|e| HttpError::NetworkError(e.to_string()))?;
301
302 if !response.status().is_success() {
303 let error_text = response
304 .text()
305 .await
306 .unwrap_or_else(|_| "Unknown error".to_string());
307 return Err(HttpError::RequestFailed(format!(
308 "Get book summary by currency failed: {}",
309 error_text
310 )));
311 }
312
313 let api_response: ApiResponse<Vec<BookSummary>> = response
314 .json()
315 .await
316 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
317
318 if let Some(error) = api_response.error {
319 return Err(HttpError::RequestFailed(format!(
320 "API error: {} - {}",
321 error.code, error.message
322 )));
323 }
324
325 api_response.result.ok_or_else(|| {
326 HttpError::InvalidResponse("No book summary data in response".to_string())
327 })
328 }
329
330 pub async fn get_instrument(&self, instrument_name: &str) -> Result<Instrument, HttpError> {
352 let url = format!(
353 "{}{}?instrument_name={}",
354 self.base_url(),
355 GET_INSTRUMENT,
356 instrument_name
357 );
358
359 let response = self
360 .http_client()
361 .get(&url)
362 .send()
363 .await
364 .map_err(|e| HttpError::NetworkError(e.to_string()))?;
365
366 if !response.status().is_success() {
367 let error_text = response
368 .text()
369 .await
370 .unwrap_or_else(|_| "Unknown error".to_string());
371 return Err(HttpError::RequestFailed(format!(
372 "Get instrument failed: {}",
373 error_text
374 )));
375 }
376
377 let api_response: ApiResponse<Instrument> = response
378 .json()
379 .await
380 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
381
382 if let Some(error) = api_response.error {
383 return Err(HttpError::RequestFailed(format!(
384 "API error: {} - {}",
385 error.code, error.message
386 )));
387 }
388
389 api_response
390 .result
391 .ok_or_else(|| HttpError::InvalidResponse("No instrument data in response".to_string()))
392 }
393
394 pub async fn get_book_summary_by_instrument(
405 &self,
406 instrument_name: &str,
407 ) -> Result<BookSummary, HttpError> {
408 let url = format!(
409 "{}{}?instrument_name={}",
410 self.base_url(),
411 GET_BOOK_SUMMARY_BY_INSTRUMENT,
412 instrument_name
413 );
414
415 let response = self
416 .http_client()
417 .get(&url)
418 .send()
419 .await
420 .map_err(|e| HttpError::NetworkError(e.to_string()))?;
421
422 if !response.status().is_success() {
423 let error_text = response
424 .text()
425 .await
426 .unwrap_or_else(|_| "Unknown error".to_string());
427 return Err(HttpError::RequestFailed(format!(
428 "Get book summary by instrument failed: {}",
429 error_text
430 )));
431 }
432
433 let api_response: ApiResponse<Vec<BookSummary>> = response
435 .json()
436 .await
437 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
438
439 if let Some(error) = api_response.error {
440 return Err(HttpError::RequestFailed(format!(
441 "API error: {} - {}",
442 error.code, error.message
443 )));
444 }
445
446 let book_summaries = api_response.result.ok_or_else(|| {
447 HttpError::InvalidResponse("No book summary data in response".to_string())
448 })?;
449
450 book_summaries.into_iter().next().ok_or_else(|| {
452 HttpError::InvalidResponse("Empty book summary array in response".to_string())
453 })
454 }
455
456 pub async fn get_contract_size(&self, instrument_name: &str) -> Result<f64, HttpError> {
478 let url = format!(
479 "{}{}?instrument_name={}",
480 self.base_url(),
481 GET_CONTRACT_SIZE,
482 instrument_name
483 );
484
485 let response = self
486 .http_client()
487 .get(&url)
488 .send()
489 .await
490 .map_err(|e| HttpError::NetworkError(e.to_string()))?;
491
492 if !response.status().is_success() {
493 let error_text = response
494 .text()
495 .await
496 .unwrap_or_else(|_| "Unknown error".to_string());
497 return Err(HttpError::RequestFailed(format!(
498 "Get contract size failed: {}",
499 error_text
500 )));
501 }
502
503 let api_response: ApiResponse<ContractSizeResponse> = response
505 .json()
506 .await
507 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
508
509 if let Some(error) = api_response.error {
510 return Err(HttpError::RequestFailed(format!(
511 "API error: {} - {}",
512 error.code, error.message
513 )));
514 }
515
516 let contract_size_response = api_response.result.ok_or_else(|| {
517 HttpError::InvalidResponse("No contract size in response".to_string())
518 })?;
519
520 Ok(contract_size_response.contract_size)
521 }
522
523 pub async fn get_server_time(&self) -> Result<u64, HttpError> {
541 let url = format!("{}{}", self.base_url(), GET_SERVER_TIME);
542
543 let response = self
544 .http_client()
545 .get(&url)
546 .send()
547 .await
548 .map_err(|e| HttpError::NetworkError(e.to_string()))?;
549
550 if !response.status().is_success() {
551 let error_text = response
552 .text()
553 .await
554 .unwrap_or_else(|_| "Unknown error".to_string());
555 return Err(HttpError::RequestFailed(format!(
556 "Get server time failed: {}",
557 error_text
558 )));
559 }
560
561 let api_response: ApiResponse<u64> = response
562 .json()
563 .await
564 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
565
566 if let Some(error) = api_response.error {
567 return Err(HttpError::RequestFailed(format!(
568 "API error: {} - {}",
569 error.code, error.message
570 )));
571 }
572
573 api_response
574 .result
575 .ok_or_else(|| HttpError::InvalidResponse("No server time in response".to_string()))
576 }
577
578 pub async fn test_connection(&self) -> Result<String, HttpError> {
583 let url = format!("{}{}", self.base_url(), TEST_CONNECTION);
584
585 let response = self
586 .http_client()
587 .get(&url)
588 .send()
589 .await
590 .map_err(|e| HttpError::NetworkError(e.to_string()))?;
591
592 if !response.status().is_success() {
593 let error_text = response
594 .text()
595 .await
596 .unwrap_or_else(|_| "Unknown error".to_string());
597 return Err(HttpError::RequestFailed(format!(
598 "Test connection failed: {}",
599 error_text
600 )));
601 }
602
603 let api_response: ApiResponse<TestResponse> = response
604 .json()
605 .await
606 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
607
608 if let Some(error) = api_response.error {
609 return Err(HttpError::RequestFailed(format!(
610 "API error: {} - {}",
611 error.code, error.message
612 )));
613 }
614
615 let test_result = api_response
616 .result
617 .ok_or_else(|| HttpError::InvalidResponse("No test result in response".to_string()))?;
618
619 Ok(test_result.version)
620 }
621
622 pub async fn get_status(&self) -> Result<StatusResponse, HttpError> {
628 let url = format!("{}{}", self.base_url(), GET_STATUS);
629
630 let response = self
631 .http_client()
632 .get(&url)
633 .send()
634 .await
635 .map_err(|e| HttpError::NetworkError(e.to_string()))?;
636
637 if !response.status().is_success() {
638 let error_text = response
639 .text()
640 .await
641 .unwrap_or_else(|_| "Unknown error".to_string());
642 return Err(HttpError::RequestFailed(format!(
643 "Get status failed: {}",
644 error_text
645 )));
646 }
647
648 match response.json::<StatusResponse>().await {
650 Ok(status) => Ok(status),
651 Err(_) => {
652 let response = self
654 .http_client()
655 .get(&url)
656 .send()
657 .await
658 .map_err(|e| HttpError::NetworkError(e.to_string()))?;
659
660 let api_response: ApiResponse<StatusResponse> = response
661 .json()
662 .await
663 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
664
665 if let Some(error) = api_response.error {
666 return Err(HttpError::RequestFailed(format!(
667 "API error: {} - {}",
668 error.code, error.message
669 )));
670 }
671
672 api_response.result.ok_or_else(|| {
673 HttpError::InvalidResponse("No status data in response".to_string())
674 })
675 }
676 }
677 }
678
679 pub async fn get_apr_history(
691 &self,
692 currency: &str,
693 limit: Option<u32>,
694 before: Option<i32>,
695 ) -> Result<AprHistoryResponse, HttpError> {
696 let mut url = format!(
697 "{}{}?currency={}",
698 self.base_url(),
699 GET_APR_HISTORY,
700 currency
701 );
702
703 if let Some(limit) = limit {
704 url.push_str(&format!("&limit={}", limit));
705 }
706
707 if let Some(before) = before {
708 url.push_str(&format!("&before={}", before));
709 }
710
711 let response = self
712 .http_client()
713 .get(&url)
714 .send()
715 .await
716 .map_err(|e| HttpError::NetworkError(e.to_string()))?;
717
718 if !response.status().is_success() {
719 let error_text = response
720 .text()
721 .await
722 .unwrap_or_else(|_| "Unknown error".to_string());
723 return Err(HttpError::RequestFailed(format!(
724 "Get APR history failed: {}",
725 error_text
726 )));
727 }
728
729 let api_response: ApiResponse<AprHistoryResponse> = response
730 .json()
731 .await
732 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
733
734 if let Some(error) = api_response.error {
735 return Err(HttpError::RequestFailed(format!(
736 "API error: {} - {}",
737 error.code, error.message
738 )));
739 }
740
741 api_response.result.ok_or_else(|| {
742 HttpError::InvalidResponse("No APR history data in response".to_string())
743 })
744 }
745
746 pub async fn get_ticker(&self, instrument_name: &str) -> Result<TickerData, HttpError> {
767 let url = format!(
768 "{}{}?instrument_name={}",
769 self.base_url(),
770 GET_TICKER,
771 instrument_name
772 );
773
774 let response = self
775 .http_client()
776 .get(&url)
777 .send()
778 .await
779 .map_err(|e| HttpError::NetworkError(e.to_string()))?;
780
781 if !response.status().is_success() {
782 let error_text = response
783 .text()
784 .await
785 .unwrap_or_else(|_| "Unknown error".to_string());
786 return Err(HttpError::RequestFailed(format!(
787 "Get ticker failed: {}",
788 error_text
789 )));
790 }
791
792 let api_response: ApiResponse<TickerData> = response
793 .json()
794 .await
795 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
796
797 if let Some(error) = api_response.error {
798 return Err(HttpError::RequestFailed(format!(
799 "API error: {} - {}",
800 error.code, error.message
801 )));
802 }
803
804 api_response
805 .result
806 .ok_or_else(|| HttpError::InvalidResponse("No ticker data in response".to_string()))
807 }
808
809 pub async fn get_order_book(
818 &self,
819 instrument_name: &str,
820 depth: Option<u32>,
821 ) -> Result<OrderBook, HttpError> {
822 let mut url = format!(
823 "{}{}?instrument_name={}",
824 self.base_url(),
825 GET_ORDER_BOOK,
826 instrument_name
827 );
828
829 if let Some(d) = depth {
830 url.push_str(&format!("&depth={}", d));
831 }
832
833 let response = self
834 .http_client()
835 .get(&url)
836 .send()
837 .await
838 .map_err(|e| HttpError::NetworkError(e.to_string()))?;
839
840 if !response.status().is_success() {
841 let error_text = response
842 .text()
843 .await
844 .unwrap_or_else(|_| "Unknown error".to_string());
845 return Err(HttpError::RequestFailed(format!(
846 "Get order book failed: {}",
847 error_text
848 )));
849 }
850
851 let api_response: ApiResponse<OrderBook> = response
852 .json()
853 .await
854 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
855
856 if let Some(error) = api_response.error {
857 return Err(HttpError::RequestFailed(format!(
858 "API error: {} - {}",
859 error.code, error.message
860 )));
861 }
862
863 api_response
864 .result
865 .ok_or_else(|| HttpError::InvalidResponse("No order book data in response".to_string()))
866 }
867
868 pub async fn get_options(
904 &self,
905 currency: &str,
906 expiry: &str,
907 ) -> Result<Vec<OptionInstrument>, HttpError> {
908 let mut instruments = self
909 .get_instruments(currency, Some("option"), Some(false))
910 .await
911 .map_err(|e| HttpError::RequestFailed(e.to_string()))?;
912
913 let base_name = format!("{}-{}", currency, expiry).to_uppercase();
914 instruments.retain(|i| i.instrument_name.starts_with(&base_name));
916
917 let mut options: Vec<OptionInstrument> = Vec::with_capacity(instruments.len());
918 for instrument in instruments {
919 let option = OptionInstrument {
920 instrument: instrument.clone(),
921 ticker: self.get_ticker(instrument.instrument_name.as_str()).await?,
922 };
923 options.push(option)
924 }
925 Ok(options)
926 }
927
928 pub async fn get_options_pair(
969 &self,
970 currency: &str,
971 expiry: &str,
972 ) -> Result<HashMap<u64, OptionInstrumentPair>, HttpError> {
973 let option_instruments = self.get_options(currency, expiry).await?;
974
975 let mut strikes_map: HashMap<u64, OptionInstrumentPair> =
976 HashMap::with_capacity(option_instruments.len() / 2);
977 for instrument in option_instruments {
978 let strike_price = instrument.instrument.strike.unwrap() as u64;
979 strikes_map
980 .entry(strike_price)
981 .or_insert(OptionInstrumentPair {
982 call: None,
983 put: None,
984 });
985 match instrument.instrument.option_type.clone() {
986 Some(option_type) => match option_type {
987 OptionType::Call => {
988 strikes_map.get_mut(&strike_price).unwrap().call = Some(instrument.clone());
989 }
990 OptionType::Put => {
991 strikes_map.get_mut(&strike_price).unwrap().put = Some(instrument.clone());
992 }
993 },
994 None => {
995 return Err(HttpError::RequestFailed(
996 "Option instrument has no option type".to_string(),
997 ));
998 }
999 }
1000 }
1001
1002 Ok(strikes_map)
1003 }
1004
1005 pub async fn get_instruments(
1015 &self,
1016 currency: &str,
1017 kind: Option<&str>,
1018 expired: Option<bool>,
1019 ) -> Result<Vec<Instrument>, HttpError> {
1020 let mut url = format!(
1021 "{}{}?currency={}",
1022 self.base_url(),
1023 GET_INSTRUMENTS,
1024 currency
1025 );
1026
1027 if let Some(k) = kind {
1028 url.push_str(&format!("&kind={}", k));
1029 }
1030
1031 if let Some(exp) = expired {
1032 url.push_str(&format!("&expired={}", exp));
1033 }
1034
1035 let response = self
1036 .http_client()
1037 .get(&url)
1038 .send()
1039 .await
1040 .map_err(|e| HttpError::NetworkError(e.to_string()))?;
1041
1042 if !response.status().is_success() {
1043 let error_text = response
1044 .text()
1045 .await
1046 .unwrap_or_else(|_| "Unknown error".to_string());
1047 return Err(HttpError::RequestFailed(format!(
1048 "Get instruments failed: {}",
1049 error_text
1050 )));
1051 }
1052
1053 let api_response: ApiResponse<Vec<Instrument>> = response
1054 .json()
1055 .await
1056 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
1057
1058 if let Some(error) = api_response.error {
1059 return Err(HttpError::RequestFailed(format!(
1060 "API error: {} - {}",
1061 error.code, error.message
1062 )));
1063 }
1064
1065 api_response.result.ok_or_else(|| {
1066 HttpError::InvalidResponse("No instruments data in response".to_string())
1067 })
1068 }
1069
1070 pub async fn get_last_trades(
1080 &self,
1081 instrument_name: &str,
1082 count: Option<u32>,
1083 include_old: Option<bool>,
1084 ) -> Result<Vec<Trade>, HttpError> {
1085 let mut url = format!(
1086 "{}{}?instrument_name={}",
1087 self.base_url(),
1088 GET_LAST_TRADES_BY_INSTRUMENT,
1089 urlencoding::encode(instrument_name)
1090 );
1091
1092 if let Some(c) = count {
1093 url.push_str(&format!("&count={}", c));
1094 }
1095
1096 if let Some(old) = include_old {
1097 url.push_str(&format!("&include_old={}", old));
1098 }
1099
1100 let response = self
1101 .http_client()
1102 .get(&url)
1103 .send()
1104 .await
1105 .map_err(|e| HttpError::NetworkError(e.to_string()))?;
1106
1107 if !response.status().is_success() {
1108 let error_text = response
1109 .text()
1110 .await
1111 .unwrap_or_else(|_| "Unknown error".to_string());
1112 return Err(HttpError::RequestFailed(format!(
1113 "Get last trades failed: {}",
1114 error_text
1115 )));
1116 }
1117
1118 let api_response: ApiResponse<LastTradesResponse> = response
1119 .json()
1120 .await
1121 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
1122
1123 if let Some(error) = api_response.error {
1124 return Err(HttpError::RequestFailed(format!(
1125 "API error: {} - {}",
1126 error.code, error.message
1127 )));
1128 }
1129
1130 let trades_response = api_response
1131 .result
1132 .ok_or_else(|| HttpError::InvalidResponse("No trades data in response".to_string()))?;
1133
1134 let trades: Vec<Trade> = trades_response
1136 .trades
1137 .into_iter()
1138 .map(|last_trade| {
1139 Trade {
1140 trade_id: last_trade.trade_id,
1141 instrument_name: last_trade.instrument_name,
1142 order_id: String::new(), direction: match last_trade.direction.as_str() {
1144 "buy" => OrderSide::Buy,
1145 "sell" => OrderSide::Sell,
1146 _ => OrderSide::Buy, },
1148 amount: last_trade.amount,
1149 price: last_trade.price,
1150 timestamp: last_trade.timestamp as i64,
1151 fee: 0.0, fee_currency: String::new(), liquidity: Liquidity::Taker, mark_price: 0.0, index_price: last_trade.index_price,
1156 instrument_kind: None, trade_seq: Some(last_trade.trade_seq),
1158 user_role: None,
1159 block_trade: None,
1160 underlying_price: None,
1161 iv: last_trade.iv,
1162 label: None,
1163 profit_loss: None,
1164 tick_direction: Some(last_trade.tick_direction),
1165 self_trade: None,
1166 }
1167 })
1168 .collect();
1169
1170 Ok(trades)
1171 }
1172
1173 pub async fn get_historical_volatility(
1191 &self,
1192 currency: &str,
1193 ) -> Result<Vec<[f64; 2]>, HttpError> {
1194 let url = format!(
1195 "{}{}?currency={}",
1196 self.base_url(),
1197 GET_HISTORICAL_VOLATILITY,
1198 urlencoding::encode(currency)
1199 );
1200
1201 let response = self
1202 .http_client()
1203 .get(&url)
1204 .send()
1205 .await
1206 .map_err(|e| HttpError::NetworkError(e.to_string()))?;
1207
1208 if !response.status().is_success() {
1209 let error_text = response
1210 .text()
1211 .await
1212 .unwrap_or_else(|_| "Unknown error".to_string());
1213 return Err(HttpError::RequestFailed(format!(
1214 "Get historical volatility failed: {}",
1215 error_text
1216 )));
1217 }
1218
1219 let api_response: ApiResponse<Vec<[f64; 2]>> = response
1220 .json()
1221 .await
1222 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
1223
1224 if let Some(error) = api_response.error {
1225 return Err(HttpError::RequestFailed(format!(
1226 "API error: {} - {}",
1227 error.code, error.message
1228 )));
1229 }
1230
1231 api_response.result.ok_or_else(|| {
1232 HttpError::InvalidResponse("No historical volatility data in response".to_string())
1233 })
1234 }
1235
1236 pub async fn get_funding_chart_data(
1255 &self,
1256 instrument_name: &str,
1257 length: &str,
1258 ) -> Result<FundingChartData, HttpError> {
1259 let url = format!(
1260 "{}{}?instrument_name={}&length={}",
1261 self.base_url(),
1262 GET_FUNDING_CHART_DATA,
1263 urlencoding::encode(instrument_name),
1264 urlencoding::encode(length)
1265 );
1266
1267 let response = self
1268 .http_client()
1269 .get(&url)
1270 .send()
1271 .await
1272 .map_err(|e| HttpError::NetworkError(e.to_string()))?;
1273
1274 if !response.status().is_success() {
1275 let error_text = response
1276 .text()
1277 .await
1278 .unwrap_or_else(|_| "Unknown error".to_string());
1279 return Err(HttpError::RequestFailed(format!(
1280 "Get funding chart data failed: {}",
1281 error_text
1282 )));
1283 }
1284
1285 let api_response: ApiResponse<FundingChartData> = response
1286 .json()
1287 .await
1288 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
1289
1290 if let Some(error) = api_response.error {
1291 return Err(HttpError::RequestFailed(format!(
1292 "API error: {} - {}",
1293 error.code, error.message
1294 )));
1295 }
1296
1297 api_response.result.ok_or_else(|| {
1298 HttpError::InvalidResponse("No funding chart data in response".to_string())
1299 })
1300 }
1301
1302 pub async fn get_tradingview_chart_data(
1323 &self,
1324 instrument_name: &str,
1325 start_timestamp: u64,
1326 end_timestamp: u64,
1327 resolution: &str,
1328 ) -> Result<TradingViewChartData, HttpError> {
1329 let url = format!(
1330 "{}{}?instrument_name={}&start_timestamp={}&end_timestamp={}&resolution={}",
1331 self.base_url(),
1332 GET_TRADINGVIEW_CHART_DATA,
1333 urlencoding::encode(instrument_name),
1334 start_timestamp,
1335 end_timestamp,
1336 urlencoding::encode(resolution)
1337 );
1338
1339 let response = self
1340 .http_client()
1341 .get(&url)
1342 .send()
1343 .await
1344 .map_err(|e| HttpError::NetworkError(e.to_string()))?;
1345
1346 if !response.status().is_success() {
1347 let error_text = response
1348 .text()
1349 .await
1350 .unwrap_or_else(|_| "Unknown error".to_string());
1351 return Err(HttpError::RequestFailed(format!(
1352 "Get TradingView chart data failed: {}",
1353 error_text
1354 )));
1355 }
1356
1357 let api_response: ApiResponse<TradingViewChartData> = response
1358 .json()
1359 .await
1360 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
1361
1362 if let Some(error) = api_response.error {
1363 return Err(HttpError::RequestFailed(format!(
1364 "API error: {} - {}",
1365 error.code, error.message
1366 )));
1367 }
1368
1369 api_response.result.ok_or_else(|| {
1370 HttpError::InvalidResponse("No TradingView chart data in response".to_string())
1371 })
1372 }
1373
1374 pub async fn get_delivery_prices(
1400 &self,
1401 index_name: &str,
1402 count: Option<u32>,
1403 offset: Option<u32>,
1404 ) -> Result<DeliveryPricesResponse, HttpError> {
1405 let mut url = format!(
1406 "{}{}?index_name={}",
1407 self.base_url(),
1408 GET_DELIVERY_PRICES,
1409 urlencoding::encode(index_name)
1410 );
1411
1412 if let Some(count) = count {
1413 url.push_str(&format!("&count={}", count));
1414 }
1415
1416 if let Some(offset) = offset {
1417 url.push_str(&format!("&offset={}", offset));
1418 }
1419
1420 let response = self
1421 .http_client()
1422 .get(&url)
1423 .send()
1424 .await
1425 .map_err(|e| HttpError::NetworkError(e.to_string()))?;
1426
1427 if !response.status().is_success() {
1428 let error_text = response
1429 .text()
1430 .await
1431 .unwrap_or_else(|_| "Unknown error".to_string());
1432 return Err(HttpError::RequestFailed(format!(
1433 "Get delivery prices failed: {}",
1434 error_text
1435 )));
1436 }
1437
1438 let api_response: ApiResponse<DeliveryPricesResponse> = response
1439 .json()
1440 .await
1441 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
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 delivery prices data in response".to_string())
1452 })
1453 }
1454
1455 pub async fn get_expirations(
1467 &self,
1468 currency: &str,
1469 kind: &str,
1470 currency_pair: Option<&str>,
1471 ) -> Result<ExpirationsResponse, HttpError> {
1472 let mut url = format!(
1473 "{}{}?currency={}&kind={}",
1474 self.base_url(),
1475 GET_EXPIRATIONS,
1476 urlencoding::encode(currency),
1477 urlencoding::encode(kind)
1478 );
1479
1480 if let Some(currency_pair) = currency_pair {
1481 url.push_str(&format!(
1482 "¤cy_pair={}",
1483 urlencoding::encode(currency_pair)
1484 ));
1485 }
1486
1487 let response = self
1488 .http_client()
1489 .get(&url)
1490 .send()
1491 .await
1492 .map_err(|e| HttpError::NetworkError(e.to_string()))?;
1493
1494 if !response.status().is_success() {
1495 let error_text = response
1496 .text()
1497 .await
1498 .unwrap_or_else(|_| "Unknown error".to_string());
1499 return Err(HttpError::RequestFailed(format!(
1500 "Get expirations failed: {}",
1501 error_text
1502 )));
1503 }
1504
1505 let api_response: ApiResponse<ExpirationsResponse> = response
1506 .json()
1507 .await
1508 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
1509
1510 if let Some(error) = api_response.error {
1511 return Err(HttpError::RequestFailed(format!(
1512 "API error: {} - {}",
1513 error.code, error.message
1514 )));
1515 }
1516
1517 api_response.result.ok_or_else(|| {
1518 HttpError::InvalidResponse("No expirations data in response".to_string())
1519 })
1520 }
1521
1522 pub async fn get_funding_rate_history(
1534 &self,
1535 instrument_name: &str,
1536 start_timestamp: u64,
1537 end_timestamp: u64,
1538 ) -> Result<Vec<FundingRateData>, HttpError> {
1539 let url = format!(
1540 "{}{}?instrument_name={}&start_timestamp={}&end_timestamp={}",
1541 self.base_url(),
1542 GET_FUNDING_RATE_HISTORY,
1543 urlencoding::encode(instrument_name),
1544 start_timestamp,
1545 end_timestamp
1546 );
1547
1548 let response = self
1549 .http_client()
1550 .get(&url)
1551 .send()
1552 .await
1553 .map_err(|e| HttpError::NetworkError(e.to_string()))?;
1554
1555 if !response.status().is_success() {
1556 let error_text = response
1557 .text()
1558 .await
1559 .unwrap_or_else(|_| "Unknown error".to_string());
1560 return Err(HttpError::RequestFailed(format!(
1561 "Get funding rate history failed: {}",
1562 error_text
1563 )));
1564 }
1565
1566 let api_response: ApiResponse<Vec<FundingRateData>> = response
1567 .json()
1568 .await
1569 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
1570
1571 if let Some(error) = api_response.error {
1572 return Err(HttpError::RequestFailed(format!(
1573 "API error: {} - {}",
1574 error.code, error.message
1575 )));
1576 }
1577
1578 api_response.result.ok_or_else(|| {
1579 HttpError::InvalidResponse("No funding rate history data in response".to_string())
1580 })
1581 }
1582
1583 pub async fn get_funding_rate_value(
1607 &self,
1608 instrument_name: &str,
1609 start_timestamp: u64,
1610 end_timestamp: u64,
1611 ) -> Result<f64, HttpError> {
1612 let url = format!(
1613 "{}{}?instrument_name={}&start_timestamp={}&end_timestamp={}",
1614 self.base_url(),
1615 GET_FUNDING_RATE_VALUE,
1616 urlencoding::encode(instrument_name),
1617 start_timestamp,
1618 end_timestamp
1619 );
1620
1621 let response = self
1622 .http_client()
1623 .get(&url)
1624 .send()
1625 .await
1626 .map_err(|e| HttpError::NetworkError(e.to_string()))?;
1627
1628 if !response.status().is_success() {
1629 let error_text = response
1630 .text()
1631 .await
1632 .unwrap_or_else(|_| "Unknown error".to_string());
1633 return Err(HttpError::RequestFailed(format!(
1634 "Get funding rate value failed: {}",
1635 error_text
1636 )));
1637 }
1638
1639 let api_response: ApiResponse<f64> = response
1640 .json()
1641 .await
1642 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
1643
1644 if let Some(error) = api_response.error {
1645 return Err(HttpError::RequestFailed(format!(
1646 "API error: {} - {}",
1647 error.code, error.message
1648 )));
1649 }
1650
1651 api_response.result.ok_or_else(|| {
1652 HttpError::InvalidResponse("No funding rate value in response".to_string())
1653 })
1654 }
1655
1656 pub async fn get_last_settlements_by_currency(
1670 &self,
1671 currency: &str,
1672 settlement_type: Option<&str>,
1673 count: Option<u32>,
1674 continuation: Option<&str>,
1675 search_start_timestamp: Option<u64>,
1676 ) -> Result<SettlementsResponse, HttpError> {
1677 let mut url = format!(
1678 "{}{}?currency={}",
1679 self.base_url(),
1680 GET_LAST_SETTLEMENTS_BY_CURRENCY,
1681 urlencoding::encode(currency)
1682 );
1683
1684 if let Some(settlement_type) = settlement_type {
1685 url.push_str(&format!("&type={}", urlencoding::encode(settlement_type)));
1686 }
1687
1688 if let Some(count) = count {
1689 url.push_str(&format!("&count={}", count));
1690 }
1691
1692 if let Some(continuation) = continuation {
1693 url.push_str(&format!(
1694 "&continuation={}",
1695 urlencoding::encode(continuation)
1696 ));
1697 }
1698
1699 if let Some(search_start_timestamp) = search_start_timestamp {
1700 url.push_str(&format!(
1701 "&search_start_timestamp={}",
1702 search_start_timestamp
1703 ));
1704 }
1705
1706 let response = self
1707 .http_client()
1708 .get(&url)
1709 .send()
1710 .await
1711 .map_err(|e| HttpError::NetworkError(e.to_string()))?;
1712
1713 if !response.status().is_success() {
1714 let error_text = response
1715 .text()
1716 .await
1717 .unwrap_or_else(|_| "Unknown error".to_string());
1718 return Err(HttpError::RequestFailed(format!(
1719 "Get last settlements by currency failed: {}",
1720 error_text
1721 )));
1722 }
1723
1724 let api_response: ApiResponse<SettlementsResponse> = response
1725 .json()
1726 .await
1727 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
1728
1729 if let Some(error) = api_response.error {
1730 return Err(HttpError::RequestFailed(format!(
1731 "API error: {} - {}",
1732 error.code, error.message
1733 )));
1734 }
1735
1736 api_response.result.ok_or_else(|| {
1737 HttpError::InvalidResponse("No settlements data in response".to_string())
1738 })
1739 }
1740
1741 pub async fn get_last_settlements_by_instrument(
1755 &self,
1756 instrument_name: &str,
1757 settlement_type: Option<&str>,
1758 count: Option<u32>,
1759 continuation: Option<&str>,
1760 search_start_timestamp: Option<u64>,
1761 ) -> Result<SettlementsResponse, HttpError> {
1762 let mut url = format!(
1763 "{}{}?instrument_name={}",
1764 self.base_url(),
1765 GET_LAST_SETTLEMENTS_BY_INSTRUMENT,
1766 urlencoding::encode(instrument_name)
1767 );
1768
1769 if let Some(settlement_type) = settlement_type {
1770 url.push_str(&format!("&type={}", urlencoding::encode(settlement_type)));
1771 }
1772
1773 if let Some(count) = count {
1774 url.push_str(&format!("&count={}", count));
1775 }
1776
1777 if let Some(continuation) = continuation {
1778 url.push_str(&format!(
1779 "&continuation={}",
1780 urlencoding::encode(continuation)
1781 ));
1782 }
1783
1784 if let Some(search_start_timestamp) = search_start_timestamp {
1785 url.push_str(&format!(
1786 "&search_start_timestamp={}",
1787 search_start_timestamp
1788 ));
1789 }
1790
1791 let response = self
1792 .http_client()
1793 .get(&url)
1794 .send()
1795 .await
1796 .map_err(|e| HttpError::NetworkError(e.to_string()))?;
1797
1798 if !response.status().is_success() {
1799 let error_text = response
1800 .text()
1801 .await
1802 .unwrap_or_else(|_| "Unknown error".to_string());
1803 return Err(HttpError::RequestFailed(format!(
1804 "Get last settlements by instrument failed: {}",
1805 error_text
1806 )));
1807 }
1808
1809 let api_response: ApiResponse<SettlementsResponse> = response
1810 .json()
1811 .await
1812 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
1813
1814 if let Some(error) = api_response.error {
1815 return Err(HttpError::RequestFailed(format!(
1816 "API error: {} - {}",
1817 error.code, error.message
1818 )));
1819 }
1820
1821 api_response.result.ok_or_else(|| {
1822 HttpError::InvalidResponse("No settlements data in response".to_string())
1823 })
1824 }
1825
1826 pub async fn get_last_trades_by_currency(
1854 &self,
1855 currency: &str,
1856 kind: Option<&str>,
1857 count: Option<u32>,
1858 include_old: Option<bool>,
1859 sorting: Option<&str>,
1860 ) -> Result<LastTradesResponse, HttpError> {
1861 let mut url = format!(
1862 "{}{}?currency={}",
1863 self.base_url(),
1864 GET_LAST_TRADES_BY_CURRENCY,
1865 urlencoding::encode(currency)
1866 );
1867
1868 if let Some(kind) = kind {
1869 url.push_str(&format!("&kind={}", urlencoding::encode(kind)));
1870 }
1871
1872 if let Some(count) = count {
1873 url.push_str(&format!("&count={}", count));
1874 }
1875
1876 if let Some(include_old) = include_old {
1877 url.push_str(&format!("&include_old={}", include_old));
1878 }
1879
1880 if let Some(sorting) = sorting {
1881 url.push_str(&format!("&sorting={}", urlencoding::encode(sorting)));
1882 }
1883
1884 let response = self
1885 .http_client()
1886 .get(&url)
1887 .send()
1888 .await
1889 .map_err(|e| HttpError::NetworkError(e.to_string()))?;
1890
1891 if !response.status().is_success() {
1892 let error_text = response
1893 .text()
1894 .await
1895 .unwrap_or_else(|_| "Unknown error".to_string());
1896 return Err(HttpError::RequestFailed(format!(
1897 "Get last trades by currency failed: {}",
1898 error_text
1899 )));
1900 }
1901
1902 let api_response: ApiResponse<LastTradesResponse> = response
1903 .json()
1904 .await
1905 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
1906
1907 if let Some(error) = api_response.error {
1908 return Err(HttpError::RequestFailed(format!(
1909 "API error: {} - {}",
1910 error.code, error.message
1911 )));
1912 }
1913
1914 api_response
1915 .result
1916 .ok_or_else(|| HttpError::InvalidResponse("No trades data in response".to_string()))
1917 }
1918
1919 #[allow(clippy::too_many_arguments)]
1949 pub async fn get_last_trades_by_currency_and_time(
1950 &self,
1951 currency: &str,
1952 start_timestamp: u64,
1953 end_timestamp: u64,
1954 kind: Option<&str>,
1955 count: Option<u32>,
1956 include_old: Option<bool>,
1957 sorting: Option<&str>,
1958 ) -> Result<LastTradesResponse, HttpError> {
1959 let mut url = format!(
1960 "{}{}?currency={}&start_timestamp={}&end_timestamp={}",
1961 self.base_url(),
1962 GET_LAST_TRADES_BY_CURRENCY_AND_TIME,
1963 urlencoding::encode(currency),
1964 start_timestamp,
1965 end_timestamp
1966 );
1967
1968 if let Some(kind) = kind {
1969 url.push_str(&format!("&kind={}", urlencoding::encode(kind)));
1970 }
1971
1972 if let Some(count) = count {
1973 url.push_str(&format!("&count={}", count));
1974 }
1975
1976 if let Some(include_old) = include_old {
1977 url.push_str(&format!("&include_old={}", include_old));
1978 }
1979
1980 if let Some(sorting) = sorting {
1981 url.push_str(&format!("&sorting={}", urlencoding::encode(sorting)));
1982 }
1983
1984 let response = self
1985 .http_client()
1986 .get(&url)
1987 .send()
1988 .await
1989 .map_err(|e| HttpError::NetworkError(e.to_string()))?;
1990
1991 if !response.status().is_success() {
1992 let error_text = response
1993 .text()
1994 .await
1995 .unwrap_or_else(|_| "Unknown error".to_string());
1996 return Err(HttpError::RequestFailed(format!(
1997 "Get last trades by currency and time failed: {}",
1998 error_text
1999 )));
2000 }
2001
2002 let api_response: ApiResponse<LastTradesResponse> = response
2003 .json()
2004 .await
2005 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
2006
2007 if let Some(error) = api_response.error {
2008 return Err(HttpError::RequestFailed(format!(
2009 "API error: {} - {}",
2010 error.code, error.message
2011 )));
2012 }
2013
2014 api_response
2015 .result
2016 .ok_or_else(|| HttpError::InvalidResponse("No trades data in response".to_string()))
2017 }
2018
2019 pub async fn get_last_trades_by_instrument_and_time(
2048 &self,
2049 instrument_name: &str,
2050 start_timestamp: u64,
2051 end_timestamp: u64,
2052 count: Option<u32>,
2053 include_old: Option<bool>,
2054 sorting: Option<&str>,
2055 ) -> Result<LastTradesResponse, HttpError> {
2056 let mut url = format!(
2057 "{}{}?instrument_name={}&start_timestamp={}&end_timestamp={}",
2058 self.base_url(),
2059 GET_LAST_TRADES_BY_INSTRUMENT_AND_TIME,
2060 urlencoding::encode(instrument_name),
2061 start_timestamp,
2062 end_timestamp
2063 );
2064
2065 if let Some(count) = count {
2066 url.push_str(&format!("&count={}", count));
2067 }
2068
2069 if let Some(include_old) = include_old {
2070 url.push_str(&format!("&include_old={}", include_old));
2071 }
2072
2073 if let Some(sorting) = sorting {
2074 url.push_str(&format!("&sorting={}", urlencoding::encode(sorting)));
2075 }
2076
2077 let response = self
2078 .http_client()
2079 .get(&url)
2080 .send()
2081 .await
2082 .map_err(|e| HttpError::NetworkError(e.to_string()))?;
2083
2084 if !response.status().is_success() {
2085 let error_text = response
2086 .text()
2087 .await
2088 .unwrap_or_else(|_| "Unknown error".to_string());
2089 return Err(HttpError::RequestFailed(format!(
2090 "Get last trades by instrument and time failed: {}",
2091 error_text
2092 )));
2093 }
2094
2095 let api_response: ApiResponse<LastTradesResponse> = response
2096 .json()
2097 .await
2098 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
2099
2100 if let Some(error) = api_response.error {
2101 return Err(HttpError::RequestFailed(format!(
2102 "API error: {} - {}",
2103 error.code, error.message
2104 )));
2105 }
2106
2107 api_response
2108 .result
2109 .ok_or_else(|| HttpError::InvalidResponse("No trades data in response".to_string()))
2110 }
2111
2112 pub async fn get_order_book_by_instrument_id(
2138 &self,
2139 instrument_id: u32,
2140 depth: Option<u32>,
2141 ) -> Result<OrderBook, HttpError> {
2142 let mut url = format!(
2143 "{}{}?instrument_id={}",
2144 self.base_url(),
2145 GET_ORDER_BOOK_BY_INSTRUMENT_ID,
2146 instrument_id
2147 );
2148
2149 if let Some(depth) = depth {
2150 url.push_str(&format!("&depth={}", depth));
2151 }
2152
2153 let response = self
2154 .http_client()
2155 .get(&url)
2156 .send()
2157 .await
2158 .map_err(|e| HttpError::NetworkError(e.to_string()))?;
2159
2160 if !response.status().is_success() {
2161 let error_text = response
2162 .text()
2163 .await
2164 .unwrap_or_else(|_| "Unknown error".to_string());
2165 return Err(HttpError::RequestFailed(format!(
2166 "Get order book by instrument ID failed: {}",
2167 error_text
2168 )));
2169 }
2170
2171 let api_response: ApiResponse<OrderBook> = response
2172 .json()
2173 .await
2174 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
2175
2176 if let Some(error) = api_response.error {
2177 return Err(HttpError::RequestFailed(format!(
2178 "API error: {} - {}",
2179 error.code, error.message
2180 )));
2181 }
2182
2183 api_response
2184 .result
2185 .ok_or_else(|| HttpError::InvalidResponse("No order book data in response".to_string()))
2186 }
2187}