1use crate::clients::errors::ClientError;
3use crate::clients::http_response_types::ResultErrorResponse;
4use crate::clients::kraken_client::KrakenClient;
5use crate::crypto::nonce_provider::NonceProvider;
6use crate::rate_limiting::keyed_rate_limits::KeyedRateLimiter;
7use crate::rate_limiting::trading_rate_limits::KrakenTradingRateLimiter;
8use crate::request_types::*;
9use crate::response_types::*;
10use crate::secrets::secrets_provider::SecretsProvider;
11use async_rate_limit::limiters::{RateLimiter, VariableCostRateLimiter};
12use async_rate_limit::sliding_window::SlidingWindowRateLimiter;
13use async_rate_limit::token_bucket::{TokenBucketRateLimiter, TokenBucketState};
14use std::collections::HashMap;
15use std::sync::Arc;
16use std::time::Duration;
17use time::OffsetDateTime;
18use tokio::sync::Mutex;
19
20#[derive(Debug, Clone)]
58pub struct RateLimitedKrakenClient<C>
59where
60 C: KrakenClient,
61{
62 core_client: C,
63 private_rate_limiter: TokenBucketRateLimiter,
64 public_rate_limiter: SlidingWindowRateLimiter,
65 trading_rate_limiter: KrakenTradingRateLimiter,
66 pair_rate_limiter: KeyedRateLimiter<String>,
67}
68
69impl<C> KrakenClient for RateLimitedKrakenClient<C>
70where
71 C: KrakenClient,
72{
73 fn new(
74 secrets_provider: Box<Arc<Mutex<dyn SecretsProvider>>>,
75 nonce_provider: Box<Arc<Mutex<dyn NonceProvider>>>,
76 ) -> RateLimitedKrakenClient<C> {
77 RateLimitedKrakenClient {
78 core_client: C::new(secrets_provider, nonce_provider),
79 private_rate_limiter: Self::get_private_rate_limiter(VerificationTier::Intermediate),
80 public_rate_limiter: Self::get_public_rate_limiter(),
81 trading_rate_limiter: KrakenTradingRateLimiter::new(VerificationTier::Intermediate),
82 pair_rate_limiter: KeyedRateLimiter::new(),
83 }
84 }
85
86 fn new_with_url(
87 secrets_provider: Box<Arc<Mutex<dyn SecretsProvider>>>,
88 nonce_provider: Box<Arc<Mutex<dyn NonceProvider>>>,
89 url: impl ToString,
90 ) -> Self {
91 RateLimitedKrakenClient {
92 core_client: C::new_with_url(secrets_provider, nonce_provider, url),
93 private_rate_limiter: Self::get_private_rate_limiter(VerificationTier::Intermediate),
94 public_rate_limiter: Self::get_public_rate_limiter(),
95 trading_rate_limiter: KrakenTradingRateLimiter::new(VerificationTier::Intermediate),
96 pair_rate_limiter: KeyedRateLimiter::new(),
97 }
98 }
99
100 fn new_with_tracing(
101 secrets_provider: Box<Arc<Mutex<dyn SecretsProvider>>>,
102 nonce_provider: Box<Arc<Mutex<dyn NonceProvider>>>,
103 trace_inbound: bool,
104 ) -> Self {
105 RateLimitedKrakenClient {
106 core_client: C::new_with_tracing(secrets_provider, nonce_provider, trace_inbound),
107 private_rate_limiter: Self::get_private_rate_limiter(VerificationTier::Intermediate),
108 public_rate_limiter: Self::get_public_rate_limiter(),
109 trading_rate_limiter: KrakenTradingRateLimiter::new(VerificationTier::Intermediate),
110 pair_rate_limiter: KeyedRateLimiter::new(),
111 }
112 }
113
114 async fn set_user_agent(&mut self, user_agent: impl ToString) {
115 self.core_client.set_user_agent(user_agent).await;
116 }
117
118 async fn get_server_time(&mut self) -> Result<ResultErrorResponse<SystemTime>, ClientError> {
119 self.public_rate_limiter.wait_until_ready().await;
120 self.core_client.get_server_time().await
121 }
122
123 async fn get_system_status(
124 &mut self,
125 ) -> Result<ResultErrorResponse<SystemStatusInfo>, ClientError> {
126 self.public_rate_limiter.wait_until_ready().await;
127 self.core_client.get_system_status().await
128 }
129
130 async fn get_asset_info(
131 &mut self,
132 request: &AssetInfoRequest,
133 ) -> Result<ResultErrorResponse<HashMap<String, AssetInfo>>, ClientError> {
134 self.public_rate_limiter.wait_until_ready().await;
135 self.core_client.get_asset_info(request).await
136 }
137
138 async fn get_tradable_asset_pairs(
139 &mut self,
140 request: &TradableAssetPairsRequest,
141 ) -> Result<ResultErrorResponse<HashMap<String, TradableAssetPair>>, ClientError> {
142 self.public_rate_limiter.wait_until_ready().await;
143 self.core_client.get_tradable_asset_pairs(request).await
144 }
145
146 async fn get_ticker_information(
147 &mut self,
148 request: &TickerRequest,
149 ) -> Result<ResultErrorResponse<HashMap<String, RestTickerInfo>>, ClientError> {
150 self.public_rate_limiter.wait_until_ready().await;
151 self.core_client.get_ticker_information(request).await
152 }
153
154 async fn get_ohlc(
155 &mut self,
156 request: &OHLCRequest,
157 ) -> Result<ResultErrorResponse<OhlcResponse>, ClientError> {
158 self.pair_rate_limiter
159 .wait_until_ready(request.pair.clone())
160 .await;
161 self.core_client.get_ohlc(request).await
162 }
163
164 async fn get_orderbook(
165 &mut self,
166 request: &OrderbookRequest,
167 ) -> Result<ResultErrorResponse<HashMap<String, Orderbook>>, ClientError> {
168 self.public_rate_limiter.wait_until_ready().await;
169 self.core_client.get_orderbook(request).await
170 }
171
172 async fn get_recent_trades(
173 &mut self,
174 request: &RecentTradesRequest,
175 ) -> Result<ResultErrorResponse<RecentTrades>, ClientError> {
176 self.pair_rate_limiter
177 .wait_until_ready(request.pair.clone())
178 .await;
179 self.public_rate_limiter.wait_until_ready().await;
180 self.core_client.get_recent_trades(request).await
181 }
182
183 async fn get_recent_spreads(
184 &mut self,
185 request: &RecentSpreadsRequest,
186 ) -> Result<ResultErrorResponse<RecentSpreads>, ClientError> {
187 self.public_rate_limiter.wait_until_ready().await;
188 self.core_client.get_recent_spreads(request).await
189 }
190
191 async fn get_account_balance(
192 &mut self,
193 ) -> Result<ResultErrorResponse<AccountBalances>, ClientError> {
194 self.private_rate_limit(100).await;
195 self.core_client.get_account_balance().await
196 }
197
198 async fn get_extended_balances(
199 &mut self,
200 ) -> Result<ResultErrorResponse<ExtendedBalances>, ClientError> {
201 self.private_rate_limit(100).await;
202 self.core_client.get_extended_balances().await
203 }
204
205 async fn get_trade_balances(
206 &mut self,
207 request: &TradeBalanceRequest,
208 ) -> Result<ResultErrorResponse<TradeBalances>, ClientError> {
209 self.private_rate_limit(100).await;
210 self.core_client.get_trade_balances(request).await
211 }
212
213 async fn get_open_orders(
214 &mut self,
215 request: &OpenOrdersRequest,
216 ) -> Result<ResultErrorResponse<OpenOrders>, ClientError> {
217 self.private_rate_limit(100).await;
218 self.core_client.get_open_orders(request).await
219 }
220
221 async fn get_closed_orders(
222 &mut self,
223 request: &ClosedOrdersRequest,
224 ) -> Result<ResultErrorResponse<ClosedOrders>, ClientError> {
225 self.private_rate_limit(200).await;
226 self.core_client.get_closed_orders(request).await
227 }
228
229 async fn query_orders_info(
230 &mut self,
231 request: &OrderRequest,
232 ) -> Result<ResultErrorResponse<HashMap<String, Order>>, ClientError> {
233 self.private_rate_limit(100).await;
234 self.core_client.query_orders_info(request).await
235 }
236
237 async fn get_order_amends(
238 &mut self,
239 request: &OrderAmendsRequest,
240 ) -> Result<ResultErrorResponse<OrderAmends>, ClientError> {
241 self.private_rate_limiter.wait_with_cost(100).await;
242 self.core_client.get_order_amends(request).await
243 }
244
245 async fn get_trades_history(
246 &mut self,
247 request: &TradesHistoryRequest,
248 ) -> Result<ResultErrorResponse<TradesHistory>, ClientError> {
249 self.private_rate_limit(200).await;
250 self.core_client.get_trades_history(request).await
251 }
252
253 async fn query_trades_info(
254 &mut self,
255 request: &TradeInfoRequest,
256 ) -> Result<ResultErrorResponse<TradesInfo>, ClientError> {
257 self.private_rate_limit(100).await;
258 self.core_client.query_trades_info(request).await
259 }
260
261 async fn get_open_positions(
262 &mut self,
263 request: &OpenPositionsRequest,
264 ) -> Result<ResultErrorResponse<OpenPositions>, ClientError> {
265 self.private_rate_limit(100).await;
266 self.core_client.get_open_positions(request).await
267 }
268
269 async fn get_ledgers_info(
270 &mut self,
271 request: &LedgersInfoRequest,
272 ) -> Result<ResultErrorResponse<LedgerInfo>, ClientError> {
273 self.private_rate_limit(200).await;
274 self.core_client.get_ledgers_info(request).await
275 }
276
277 async fn query_ledgers(
278 &mut self,
279 request: &QueryLedgerRequest,
280 ) -> Result<ResultErrorResponse<QueryLedgerInfo>, ClientError> {
281 self.private_rate_limit(100).await;
282 self.core_client.query_ledgers(request).await
283 }
284
285 async fn get_trade_volume(
286 &mut self,
287 request: &TradeVolumeRequest,
288 ) -> Result<ResultErrorResponse<TradeVolume>, ClientError> {
289 self.private_rate_limit(100).await;
290 self.core_client.get_trade_volume(request).await
291 }
292
293 async fn request_export_report(
294 &mut self,
295 request: &ExportReportRequest,
296 ) -> Result<ResultErrorResponse<ExportReport>, ClientError> {
297 self.private_rate_limit(100).await;
298 self.core_client.request_export_report(request).await
299 }
300
301 async fn get_export_report_status(
302 &mut self,
303 request: &ExportReportStatusRequest,
304 ) -> Result<ResultErrorResponse<Vec<ExportReportStatus>>, ClientError> {
305 self.private_rate_limit(100).await;
306 self.core_client.get_export_report_status(request).await
307 }
308
309 async fn retrieve_export_report(
310 &mut self,
311 request: &RetrieveExportReportRequest,
312 ) -> Result<Vec<u8>, ClientError> {
313 self.private_rate_limit(100).await;
314 self.core_client.retrieve_export_report(request).await
315 }
316
317 async fn delete_export_report(
318 &mut self,
319 request: &DeleteExportRequest,
320 ) -> Result<ResultErrorResponse<DeleteExportReport>, ClientError> {
321 self.private_rate_limit(100).await;
322 self.core_client.delete_export_report(request).await
323 }
324
325 async fn add_order(
326 &mut self,
327 request: &AddOrderRequest,
328 ) -> Result<ResultErrorResponse<AddOrder>, ClientError> {
329 self.trading_rate_limiter.add_order().await;
330 let response = self.core_client.add_order(request).await;
331 self.notify_add_order(&response, request.user_ref, &request.client_order_id)
332 .await;
333
334 response
335 }
336
337 async fn add_order_batch(
338 &mut self,
339 request: &AddBatchedOrderRequest,
340 ) -> Result<ResultErrorResponse<AddOrderBatch>, ClientError> {
341 self.trading_rate_limiter.add_order_batch(request).await;
342 let response = self.core_client.add_order_batch(request).await;
343 self.notify_add_order_batched(&response, request).await;
344
345 response
346 }
347
348 async fn amend_order(
349 &mut self,
350 request: &AmendOrderRequest,
351 ) -> Result<ResultErrorResponse<AmendOrder>, ClientError> {
352 self.trading_rate_limiter
353 .amend_order(&request.tx_id, &request.client_order_id)
354 .await;
355 let response = self.core_client.amend_order(request).await;
356 self.notify_amend_order(&request.tx_id, &request.client_order_id.clone())
357 .await;
358
359 response
360 }
361
362 async fn edit_order(
363 &mut self,
364 request: &EditOrderRequest,
365 ) -> Result<ResultErrorResponse<OrderEdit>, ClientError> {
366 self.trading_rate_limiter.edit_order(request).await;
367 let response = self.core_client.edit_order(request).await;
368 self.notify_edit_order(&response, request.user_ref).await;
369 response
370 }
371
372 async fn cancel_order(
373 &mut self,
374 request: &CancelOrderRequest,
375 ) -> Result<ResultErrorResponse<CancelOrder>, ClientError> {
376 match &request.tx_id {
377 IntOrString::Int(i) => {
378 self.trading_rate_limiter.cancel_order_user_ref(i).await;
379 }
380 IntOrString::String(s) => {
381 self.trading_rate_limiter.cancel_order_tx_id(s).await;
382 }
383 }
384
385 self.core_client.cancel_order(request).await
386 }
387
388 async fn cancel_all_orders(&mut self) -> Result<ResultErrorResponse<CancelOrder>, ClientError> {
389 self.core_client.cancel_all_orders().await
390 }
391
392 async fn cancel_all_orders_after(
393 &mut self,
394 request: &CancelAllOrdersAfterRequest,
395 ) -> Result<ResultErrorResponse<CancelAllOrdersAfter>, ClientError> {
396 self.core_client.cancel_all_orders_after(request).await
397 }
398
399 async fn cancel_order_batch(
402 &mut self,
403 request: &CancelBatchOrdersRequest,
404 ) -> Result<ResultErrorResponse<CancelOrder>, ClientError> {
405 for order in &request.orders {
406 match order {
407 IntOrString::Int(user_ref) => {
408 self.trading_rate_limiter
409 .cancel_order_user_ref(user_ref)
410 .await
411 }
412 IntOrString::String(tx_id) => {
413 self.trading_rate_limiter.cancel_order_tx_id(tx_id).await
414 }
415 }
416 }
417
418 self.core_client.cancel_order_batch(request).await
419 }
420
421 async fn get_deposit_methods(
422 &mut self,
423 request: &DepositMethodsRequest,
424 ) -> Result<ResultErrorResponse<Vec<DepositMethod>>, ClientError> {
425 self.private_rate_limit(100).await;
426 self.core_client.get_deposit_methods(request).await
427 }
428
429 async fn get_deposit_addresses(
430 &mut self,
431 request: &DepositAddressesRequest,
432 ) -> Result<ResultErrorResponse<Vec<DepositAddress>>, ClientError> {
433 self.private_rate_limit(100).await;
434 self.core_client.get_deposit_addresses(request).await
435 }
436
437 async fn get_status_of_recent_deposits(
438 &mut self,
439 request: &StatusOfDepositWithdrawRequest,
440 ) -> Result<ResultErrorResponse<DepositWithdrawResponse>, ClientError> {
441 self.private_rate_limit(100).await;
442 self.core_client
443 .get_status_of_recent_deposits(request)
444 .await
445 }
446
447 async fn get_withdrawal_methods(
448 &mut self,
449 request: &WithdrawalMethodsRequest,
450 ) -> Result<ResultErrorResponse<Vec<WithdrawMethod>>, ClientError> {
451 self.private_rate_limit(100).await;
452 self.core_client.get_withdrawal_methods(request).await
453 }
454
455 async fn get_withdrawal_addresses(
456 &mut self,
457 request: &WithdrawalAddressesRequest,
458 ) -> Result<ResultErrorResponse<Vec<WithdrawalAddress>>, ClientError> {
459 self.private_rate_limit(100).await;
460 self.core_client.get_withdrawal_addresses(request).await
461 }
462
463 async fn get_withdrawal_info(
464 &mut self,
465 request: &WithdrawalInfoRequest,
466 ) -> Result<ResultErrorResponse<Withdrawal>, ClientError> {
467 self.private_rate_limit(100).await;
468 self.core_client.get_withdrawal_info(request).await
469 }
470
471 async fn withdraw_funds(
472 &mut self,
473 request: &WithdrawFundsRequest,
474 ) -> Result<ResultErrorResponse<ConfirmationRefId>, ClientError> {
475 self.private_rate_limit(100).await;
476 self.core_client.withdraw_funds(request).await
477 }
478
479 async fn get_status_of_recent_withdrawals(
480 &mut self,
481 request: &StatusOfDepositWithdrawRequest,
482 ) -> Result<ResultErrorResponse<Vec<DepositWithdrawal>>, ClientError> {
483 self.private_rate_limit(100).await;
484 self.core_client
485 .get_status_of_recent_withdrawals(request)
486 .await
487 }
488
489 async fn request_withdrawal_cancellation(
490 &mut self,
491 request: &WithdrawCancelRequest,
492 ) -> Result<ResultErrorResponse<bool>, ClientError> {
493 self.private_rate_limit(100).await;
494 self.core_client
495 .request_withdrawal_cancellation(request)
496 .await
497 }
498
499 async fn request_wallet_transfer(
500 &mut self,
501 request: &WalletTransferRequest,
502 ) -> Result<ResultErrorResponse<ConfirmationRefId>, ClientError> {
503 self.private_rate_limit(100).await;
504 self.core_client.request_wallet_transfer(request).await
505 }
506
507 async fn create_sub_account(
508 &mut self,
509 request: &CreateSubAccountRequest,
510 ) -> Result<ResultErrorResponse<bool>, ClientError> {
511 self.private_rate_limit(100).await;
512 self.core_client.create_sub_account(request).await
513 }
514
515 async fn account_transfer(
516 &mut self,
517 request: &AccountTransferRequest,
518 ) -> Result<ResultErrorResponse<AccountTransfer>, ClientError> {
519 self.private_rate_limit(100).await;
520 self.core_client.account_transfer(request).await
521 }
522
523 async fn allocate_earn_funds(
524 &mut self,
525 request: &AllocateEarnFundsRequest,
526 ) -> Result<ResultErrorResponse<bool>, ClientError> {
527 self.private_rate_limit(100).await;
528 self.core_client.allocate_earn_funds(request).await
529 }
530
531 async fn deallocate_earn_funds(
532 &mut self,
533 request: &AllocateEarnFundsRequest,
534 ) -> Result<ResultErrorResponse<bool>, ClientError> {
535 self.private_rate_limit(100).await;
536 self.core_client.deallocate_earn_funds(request).await
537 }
538
539 async fn get_earn_allocation_status(
540 &mut self,
541 request: &EarnAllocationStatusRequest,
542 ) -> Result<ResultErrorResponse<AllocationStatus>, ClientError> {
543 self.private_rate_limit(100).await;
544 self.core_client.get_earn_allocation_status(request).await
545 }
546
547 async fn get_earn_deallocation_status(
548 &mut self,
549 request: &EarnAllocationStatusRequest,
550 ) -> Result<ResultErrorResponse<AllocationStatus>, ClientError> {
551 self.private_rate_limit(100).await;
552 self.core_client.get_earn_deallocation_status(request).await
553 }
554
555 async fn list_earn_strategies(
556 &mut self,
557 request: &ListEarnStrategiesRequest,
558 ) -> Result<ResultErrorResponse<EarnStrategies>, ClientError> {
559 self.private_rate_limit(100).await;
560 self.core_client.list_earn_strategies(request).await
561 }
562
563 async fn list_earn_allocations(
564 &mut self,
565 request: &ListEarnAllocationsRequest,
566 ) -> Result<ResultErrorResponse<EarnAllocations>, ClientError> {
567 self.private_rate_limit(100).await;
568 self.core_client.list_earn_allocations(request).await
569 }
570
571 async fn get_websockets_token(
572 &mut self,
573 ) -> Result<ResultErrorResponse<WebsocketToken>, ClientError> {
574 self.private_rate_limit(100).await;
575 self.core_client.get_websockets_token().await
576 }
577}
578
579impl<C> RateLimitedKrakenClient<C>
580where
581 C: KrakenClient,
582{
583 async fn notify_add_order(
586 &mut self,
587 order_response: &Result<ResultErrorResponse<AddOrder>, ClientError>,
588 user_ref: Option<i64>,
589 client_order_id: &Option<String>,
590 ) {
591 if let Ok(ResultErrorResponse {
592 result: Some(result),
593 ..
594 }) = order_response
595 {
596 for tx_id in &result.tx_id {
597 self.trading_rate_limiter
598 .notify_add_order(
599 tx_id.clone(),
600 OffsetDateTime::now_utc().unix_timestamp(),
601 user_ref,
602 client_order_id,
603 )
604 .await;
605 }
606 }
607 }
608
609 async fn notify_amend_order(
612 &mut self,
613 tx_id: &Option<String>,
614 client_order_id: &Option<String>,
615 ) {
616 self.trading_rate_limiter
617 .notify_amend_order(
618 tx_id,
619 OffsetDateTime::now_utc().unix_timestamp(),
620 client_order_id,
621 )
622 .await;
623 }
624
625 async fn notify_add_order_batched(
628 &mut self,
629 order_response: &Result<ResultErrorResponse<AddOrderBatch>, ClientError>,
630 request: &AddBatchedOrderRequest,
631 ) {
632 if let Ok(ResultErrorResponse {
633 result: Some(result),
634 ..
635 }) = order_response
636 {
637 for (order, request) in result.orders.iter().zip(request.orders.iter()) {
638 self.trading_rate_limiter
639 .notify_add_order(
640 order.tx_id.clone(),
641 OffsetDateTime::now_utc().unix_timestamp(),
642 request.user_ref,
643 &request.client_order_id,
644 )
645 .await
646 }
647 }
648 }
649
650 async fn notify_edit_order(
653 &mut self,
654 order_response: &Result<ResultErrorResponse<OrderEdit>, ClientError>,
655 user_ref: Option<i64>,
656 ) {
657 if let Ok(ResultErrorResponse {
658 result: Some(result),
659 ..
660 }) = order_response
661 {
662 self.trading_rate_limiter
663 .notify_add_order(
664 result.tx_id.clone(),
665 OffsetDateTime::now_utc().unix_timestamp(),
666 user_ref,
667 &None,
668 )
669 .await
670 }
671 }
672}
673
674impl<C> RateLimitedKrakenClient<C>
675where
676 C: KrakenClient,
677{
678 pub fn new_with_client(
680 client: C,
681 verification: VerificationTier,
682 ) -> RateLimitedKrakenClient<C> {
683 RateLimitedKrakenClient {
684 core_client: client,
685 private_rate_limiter: Self::get_private_rate_limiter(verification),
686 public_rate_limiter: Self::get_public_rate_limiter(),
687 trading_rate_limiter: KrakenTradingRateLimiter::new(verification),
688 pair_rate_limiter: KeyedRateLimiter::new(),
689 }
690 }
691
692 pub fn new_with_verification_tier(
694 secrets_provider: Box<Arc<Mutex<dyn SecretsProvider>>>,
695 nonce_provider: Box<Arc<Mutex<dyn NonceProvider>>>,
696 verification: VerificationTier,
697 ) -> Self {
698 RateLimitedKrakenClient {
699 core_client: C::new(secrets_provider, nonce_provider),
700 private_rate_limiter: Self::get_private_rate_limiter(verification),
701 public_rate_limiter: Self::get_public_rate_limiter(),
702 trading_rate_limiter: KrakenTradingRateLimiter::new(verification),
703 pair_rate_limiter: KeyedRateLimiter::new(),
704 }
705 }
706
707 pub fn new_with_verification_tier_and_url(
709 secrets_provider: Box<Arc<Mutex<dyn SecretsProvider>>>,
710 nonce_provider: Box<Arc<Mutex<dyn NonceProvider>>>,
711 url: String,
712 verification: VerificationTier,
713 ) -> Self {
714 RateLimitedKrakenClient {
715 core_client: C::new_with_url(secrets_provider, nonce_provider, url),
716 private_rate_limiter: Self::get_private_rate_limiter(verification),
717 public_rate_limiter: Self::get_public_rate_limiter(),
718 trading_rate_limiter: KrakenTradingRateLimiter::new(verification),
719 pair_rate_limiter: KeyedRateLimiter::new(),
720 }
721 }
722
723 pub fn get_private_rate_limiter(user_verification: VerificationTier) -> TokenBucketRateLimiter {
727 match user_verification {
729 VerificationTier::Intermediate => {
730 let token_bucket_state = TokenBucketState::new(2000, 50, Duration::from_secs(1));
731 TokenBucketRateLimiter::new(Arc::new(Mutex::new(token_bucket_state)))
732 }
733 VerificationTier::Pro => {
734 let token_bucket_state = TokenBucketState::new(2000, 100, Duration::from_secs(1));
735 TokenBucketRateLimiter::new(Arc::new(Mutex::new(token_bucket_state)))
736 }
737 }
738 }
739
740 pub fn get_public_rate_limiter() -> SlidingWindowRateLimiter {
742 SlidingWindowRateLimiter::new(Duration::from_secs(1), 1)
743 }
744
745 async fn private_rate_limit(&mut self, cost: usize) {
746 self.private_rate_limiter.wait_with_cost(cost).await
747 }
748}
749
750#[cfg(test)]
751mod tests {
752 use crate::clients::core_kraken_client::CoreKrakenClient;
753 use crate::clients::kraken_client::endpoints::KRAKEN_BASE_URL;
754 use crate::clients::kraken_client::KrakenClient;
755 use crate::clients::rate_limited_kraken_client::RateLimitedKrakenClient;
756 use crate::crypto::nonce_provider::{IncreasingNonceProvider, NonceProvider};
757 use crate::request_types::{
758 AccountTransferRequest, AddBatchedOrderRequest, AddOrderRequest, AllocateEarnFundsRequest,
759 AmendOrderRequest, AssetInfoRequestBuilder, BatchedOrderRequest, CancelBatchOrdersRequest,
760 CancelOrderRequest, CandlestickInterval, ClosedOrdersRequestBuilder,
761 CreateSubAccountRequest, DeleteExportRequest, DeleteExportType, DepositAddressesRequest,
762 DepositMethodsRequest, EarnAllocationStatusRequest, EditOrderRequest, ExportReportRequest,
763 ExportReportStatusRequest, IntOrString, LedgersInfoRequest, ListEarnAllocationsRequest,
764 ListEarnStrategiesRequest, OHLCRequest, OpenOrdersRequest, OpenPositionsRequest,
765 OrderFlags, OrderRequest, OrderbookRequest, QueryLedgerRequest, RecentSpreadsRequest,
766 RecentTradesRequest, ReportFormatType, ReportType, RetrieveExportReportRequest,
767 StatusOfDepositWithdrawRequest, StringCSV, TickerRequest, TradableAssetPairsRequest,
768 TradeBalanceRequest, TradeInfoRequest, TradeVolumeRequest, TradesHistoryRequest,
769 WalletTransferRequest, WithdrawCancelRequest, WithdrawFundsRequest,
770 WithdrawalAddressesRequest, WithdrawalInfoRequest, WithdrawalMethodsRequest,
771 };
772 use crate::response_types::VerificationTier::{Intermediate, Pro};
773 use crate::response_types::{AddOrder, BuySell, OrderFlag, OrderType, VerificationTier};
774 use crate::secrets::secrets_provider::StaticSecretsProvider;
775 use crate::test_data::public_response_json::get_server_time_json;
776 use crate::test_data::TestRateLimitedClient;
777 use crate::test_data::{
778 get_null_secrets_provider, get_rate_limit_test_client, get_rate_limit_test_client_err,
779 };
780 use crate::test_rate_limited_endpoint;
781 use rust_decimal_macros::dec;
782 use std::sync::Arc;
783 use std::time::Duration;
784 use tokio::sync::Mutex;
785 use tokio::time::pause;
786 use tokio::time::Instant;
787 use wiremock::matchers::{header, method, path};
788 use wiremock::{Mock, MockServer, ResponseTemplate};
789
790 #[test]
791 fn client_creates() {
792 let secrets_provider = StaticSecretsProvider::new("", "");
793 let nonce_provider: Box<Arc<Mutex<dyn NonceProvider>>> =
794 Box::new(Arc::new(Mutex::new(IncreasingNonceProvider::new())));
795 let client: RateLimitedKrakenClient<CoreKrakenClient> = RateLimitedKrakenClient::new(
796 Box::new(Arc::new(Mutex::new(secrets_provider))),
797 nonce_provider,
798 );
799
800 assert_eq!(client.core_client.api_url, KRAKEN_BASE_URL);
801 }
802
803 #[tokio::test]
804 async fn client_user_agent() {
805 let secrets_provider = get_null_secrets_provider();
806 let nonce_provider: Box<Arc<Mutex<dyn NonceProvider>>> =
807 Box::new(Arc::new(Mutex::new(IncreasingNonceProvider::new())));
808 let mock_server = MockServer::start().await;
809 let mut client: RateLimitedKrakenClient<CoreKrakenClient> =
810 RateLimitedKrakenClient::new_with_url(
811 secrets_provider,
812 nonce_provider,
813 mock_server.uri(),
814 );
815
816 Mock::given(method("GET"))
817 .and(path("/0/public/Time"))
818 .and(header("user-agent", "KrakenAsyncRsClient"))
819 .respond_with(ResponseTemplate::new(200).set_body_json(get_server_time_json()))
820 .expect(1)
821 .mount(&mock_server)
822 .await;
823
824 let _resp = client.get_server_time().await;
825 mock_server.verify().await;
826
827 client.set_user_agent("Strategy#1".to_string()).await;
828
829 Mock::given(method("GET"))
830 .and(path("/0/public/Time"))
831 .and(header("user-agent", "Strategy#1"))
832 .respond_with(ResponseTemplate::new(200).set_body_json(get_server_time_json()))
833 .expect(1)
834 .mount(&mock_server)
835 .await;
836
837 let _resp = client.get_server_time().await;
838 mock_server.verify().await;
839 }
840
841 #[tokio::test]
842 async fn test_system_public_endpoints() {
843 pause();
844 let n_calls = 7;
845
846 test_rate_limited_endpoint!(get_server_time, n_calls, n_calls - 1, n_calls, Intermediate);
848
849 test_rate_limited_endpoint!(
850 get_system_status,
851 n_calls,
852 n_calls - 1,
853 n_calls,
854 Intermediate
855 );
856 }
857
858 #[tokio::test]
859 async fn test_get_asset_info() {
860 pause();
861 let n_calls = 7;
862
863 let pairs = StringCSV::new(vec![
864 "XBT".to_string(),
865 "ETH".to_string(),
866 "ZUSD".to_string(),
867 ]);
868 let request = AssetInfoRequestBuilder::new()
869 .asset(pairs)
870 .asset_class("currency".into())
871 .build();
872
873 test_rate_limited_endpoint!(
875 get_asset_info,
876 n_calls,
877 n_calls - 1,
878 n_calls,
879 Intermediate,
880 &request
881 );
882 }
883
884 #[tokio::test]
885 async fn test_get_tradable_asset_pairs() {
886 pause();
887 let n_calls = 7;
888
889 let pairs = StringCSV::new(vec!["ETHUSD".to_string()]);
890 let request = TradableAssetPairsRequest::builder().pair(pairs).build();
891
892 test_rate_limited_endpoint!(
894 get_tradable_asset_pairs,
895 n_calls,
896 n_calls - 1,
897 n_calls,
898 Intermediate,
899 &request
900 );
901 }
902
903 #[tokio::test]
904 async fn test_get_ticker_information() {
905 pause();
906 let n_calls = 7;
907
908 let pairs = StringCSV::new(vec![
909 "BTCUSD".to_string(),
910 "ETHUSD".to_string(),
911 "USDCUSD".to_string(),
912 ]);
913 let request = TickerRequest::builder().pair(pairs).build();
914
915 test_rate_limited_endpoint!(
917 get_ticker_information,
918 n_calls,
919 n_calls - 1,
920 n_calls,
921 Intermediate,
922 &request
923 );
924 }
925
926 #[tokio::test]
927 async fn test_get_ohlc_and_recent_trades() {
928 pause();
929 let n_calls = 7;
930
931 let ohlc_request = OHLCRequest::builder("XETHZUSD".to_string())
932 .interval(CandlestickInterval::Hour)
933 .build();
934
935 let trades_request = RecentTradesRequest::builder("XXBTZUSD".to_string())
936 .count(10)
937 .build();
938
939 let secrets_provider = get_null_secrets_provider();
940 let nonce_provider: Box<Arc<Mutex<dyn NonceProvider>>> =
941 Box::new(Arc::new(Mutex::new(IncreasingNonceProvider::new())));
942
943 let mut client: TestRateLimitedClient = RateLimitedKrakenClient::new_with_verification_tier(
944 secrets_provider,
945 nonce_provider,
946 Pro,
947 );
948
949 let start = Instant::now();
950
951 for _ in 0..n_calls {
953 let _ = client.get_ohlc(&ohlc_request).await;
954 let _ = client.get_recent_trades(&trades_request).await;
955 }
956
957 let end = Instant::now();
958 let elapsed = end - start;
959
960 println!("{:?}", elapsed);
961
962 assert!(elapsed > Duration::from_secs(n_calls - 1));
963 assert!(elapsed < Duration::from_secs(n_calls));
964 }
965
966 #[tokio::test]
967 async fn test_get_orderbook() {
968 pause();
969 let n_calls = 7;
970
971 let request = OrderbookRequest::builder("XXBTZUSD".to_string())
972 .count(10)
973 .build();
974
975 test_rate_limited_endpoint!(
977 get_orderbook,
978 n_calls,
979 n_calls - 1,
980 n_calls,
981 Intermediate,
982 &request
983 );
984 }
985
986 #[tokio::test]
987 async fn test_get_recent_trades() {
988 pause();
989 let n_calls = 7;
990
991 let request = RecentTradesRequest::builder("XXBTZUSD".to_string())
992 .count(10)
993 .build();
994
995 test_rate_limited_endpoint!(
997 get_recent_trades,
998 n_calls,
999 n_calls - 1,
1000 n_calls,
1001 Intermediate,
1002 &request
1003 );
1004 }
1005
1006 #[tokio::test]
1007 async fn test_get_recent_spreads() {
1008 pause();
1009 let n_calls = 7;
1010
1011 let request = RecentSpreadsRequest::builder("XXBTZUSD".to_string())
1012 .since(0)
1013 .build();
1014 test_rate_limited_endpoint!(
1016 get_recent_spreads,
1017 n_calls,
1018 n_calls - 1,
1019 n_calls,
1020 Intermediate,
1021 &request
1022 );
1023 }
1024
1025 #[tokio::test]
1026 async fn test_get_account_balance() {
1027 pause();
1028
1029 test_rate_limited_endpoint!(get_account_balance, 22, 4, 5, Intermediate);
1031 }
1032
1033 #[tokio::test]
1034 async fn test_get_extended_balance() {
1035 pause();
1036
1037 test_rate_limited_endpoint!(get_extended_balances, 22, 2, 3, Pro);
1039 }
1040
1041 #[tokio::test]
1042 async fn test_get_trade_balances() {
1043 pause();
1044
1045 let request = TradeBalanceRequest::builder()
1046 .asset("XXBTZUSD".to_string())
1047 .build();
1048
1049 test_rate_limited_endpoint!(get_trade_balances, 26, 6, 7, Pro, &request);
1051 }
1052
1053 #[tokio::test]
1054 async fn test_get_open_orders() {
1055 pause();
1056
1057 let request = OpenOrdersRequest::builder().trades(true).build();
1058
1059 test_rate_limited_endpoint!(get_open_orders, 23, 6, 7, Intermediate, &request);
1061 }
1062
1063 #[tokio::test]
1064 async fn test_get_closed_orders() {
1065 pause();
1066
1067 let request = ClosedOrdersRequestBuilder::new()
1068 .trades(true)
1069 .start(12340000)
1070 .build();
1071
1072 test_rate_limited_endpoint!(get_closed_orders, 13, 6, 7, Pro, &request);
1074 }
1075
1076 #[tokio::test]
1077 async fn test_query_orders_info() {
1078 pause();
1079
1080 let tx_ids = StringCSV::new(vec!["uuid_1".to_string()]);
1081
1082 let request = OrderRequest::builder(tx_ids)
1083 .trades(true)
1084 .consolidate_taker(false)
1085 .build();
1086
1087 test_rate_limited_endpoint!(query_orders_info, 26, 12, 13, Intermediate, &request);
1089 }
1090
1091 #[tokio::test]
1092 async fn test_get_trades_history() {
1093 pause();
1094
1095 let request = TradesHistoryRequest::builder()
1096 .start(0)
1097 .end(1234)
1098 .trades(true)
1099 .consolidate_taker(false)
1100 .build();
1101
1102 test_rate_limited_endpoint!(get_trades_history, 14, 8, 9, Pro, &request);
1104 }
1105
1106 #[tokio::test]
1107 async fn test_query_trades_info() {
1108 pause();
1109
1110 let tx_ids = StringCSV::new(vec!["some-unique-id".to_string()]);
1111
1112 let request = TradeInfoRequest::builder(tx_ids).trades(true).build();
1113
1114 test_rate_limited_endpoint!(query_trades_info, 25, 10, 11, Intermediate, &request);
1116 }
1117
1118 #[tokio::test]
1119 async fn test_get_open_positions() {
1120 pause();
1121
1122 let request = OpenPositionsRequest::builder()
1123 .do_calcs(true)
1124 .consolidation("market".to_string())
1125 .build();
1126
1127 test_rate_limited_endpoint!(get_open_positions, 25, 5, 6, Pro, &request);
1129 }
1130
1131 #[tokio::test]
1132 async fn test_get_ledgers_info() {
1133 pause();
1134
1135 let request = LedgersInfoRequest::builder()
1136 .start(0)
1137 .asset(StringCSV(vec!["all".into()]))
1138 .build();
1139
1140 test_rate_limited_endpoint!(get_ledgers_info, 12, 8, 9, Intermediate, &request);
1142 }
1143
1144 #[tokio::test]
1145 async fn test_query_ledgers() {
1146 pause();
1147
1148 let request = QueryLedgerRequest::builder(StringCSV(vec!["51AHCZ-XXZ64-YW34UP".into()]))
1149 .trades(true)
1150 .build();
1151
1152 test_rate_limited_endpoint!(query_ledgers, 24, 4, 5, Pro, &request);
1154 }
1155
1156 #[tokio::test]
1157 async fn test_get_trade_volume() {
1158 pause();
1159
1160 let request = TradeVolumeRequest::builder()
1161 .pair(StringCSV(vec!["XXBTZUSD".to_string()]))
1162 .build();
1163
1164 test_rate_limited_endpoint!(get_trade_volume, 24, 8, 9, Intermediate, &request);
1166 }
1167
1168 #[tokio::test]
1169 async fn test_request_export_report() {
1170 pause();
1171
1172 let request = ExportReportRequest::builder(ReportType::Ledgers, "TestExport".to_string())
1173 .format(ReportFormatType::Csv)
1174 .build();
1175
1176 test_rate_limited_endpoint!(request_export_report, 24, 4, 5, Pro, &request);
1178 }
1179
1180 #[tokio::test]
1181 async fn test_get_export_report_status() {
1182 pause();
1183
1184 let request = ExportReportStatusRequest::builder(ReportType::Trades).build();
1185
1186 test_rate_limited_endpoint!(get_export_report_status, 27, 14, 15, Intermediate, &request);
1188 }
1189
1190 #[tokio::test]
1191 async fn test_retrieve_export_report() {
1192 pause();
1193
1194 let request =
1195 RetrieveExportReportRequest::builder("HI1M0S-BCRBJ-P01V9R".to_string()).build();
1196
1197 test_rate_limited_endpoint!(retrieve_export_report, 24, 4, 5, Pro, &request);
1199 }
1200
1201 #[tokio::test]
1202 async fn test_delete_export_report() {
1203 pause();
1204
1205 let request =
1206 DeleteExportRequest::builder("54E7".to_string(), DeleteExportType::Delete).build();
1207
1208 test_rate_limited_endpoint!(delete_export_report, 24, 8, 9, Intermediate, &request);
1210 }
1211
1212 #[tokio::test]
1213 async fn test_adding_order_limits() {
1214 pause();
1215 let mut client = get_rate_limit_test_client(Pro);
1216 let mut client_err = get_rate_limit_test_client_err(Pro);
1217
1218 let start = Instant::now();
1219
1220 let request = get_add_order_request();
1221
1222 for _ in 0..(180 + 15) {
1225 let _ = client.add_order(&request).await;
1226 let _ = client_err.add_order(&request).await;
1227 }
1228
1229 let end = Instant::now();
1230 let elapsed = end - start;
1231 println!("{:?}", elapsed);
1232
1233 assert!(elapsed > Duration::from_secs(4));
1234 assert!(elapsed < Duration::from_secs(5));
1235 }
1236
1237 #[tokio::test]
1238 async fn test_amend_order_max_penalty() {
1239 pause();
1240 let verification = Intermediate;
1241 let mut client = get_rate_limit_test_client(verification);
1242
1243 let orders = max_out_rate_limits(&mut client, verification).await;
1244
1245 let amend_start = Instant::now();
1246
1247 for i in 0..4 {
1249 let amend_request = get_amend_for_order(&orders, i);
1250 let _ = client.amend_order(&amend_request).await;
1251 }
1252
1253 let amend_elapsed = amend_start.elapsed();
1254 println!("{:?}", amend_elapsed);
1255
1256 assert!(amend_elapsed > Duration::from_secs(7));
1257 assert!(amend_elapsed < Duration::from_secs(8));
1258 }
1259
1260 fn get_amend_for_order(orders: &Vec<AddOrder>, i: usize) -> AmendOrderRequest {
1261 AmendOrderRequest::builder()
1262 .tx_id(orders.get(i).unwrap().tx_id.first().unwrap().clone()) .build()
1264 }
1265
1266 #[tokio::test]
1267 async fn test_add_order_batch_limits() {
1268 pause();
1269 let mut client = get_rate_limit_test_client(Pro);
1270 let mut client_err = get_rate_limit_test_client_err(Pro);
1271
1272 let start = Instant::now();
1273
1274 let request = get_batched_order_request(16);
1275
1276 for _ in 0..21 {
1279 let _ = client.add_order_batch(&request).await;
1280 let _ = client_err.add_order_batch(&request).await;
1281 }
1282
1283 let end = Instant::now();
1284 let elapsed = end - start;
1285 println!("{:?}", elapsed);
1286
1287 assert!(elapsed > Duration::from_secs(3));
1288 assert!(elapsed < Duration::from_secs(4));
1289 }
1290
1291 #[tokio::test]
1292 async fn test_edit_order_max_penalty() {
1293 pause();
1294 let verification = Pro;
1295 let mut client = get_rate_limit_test_client(verification);
1296 let mut client_err = get_rate_limit_test_client_err(Pro);
1297
1298 let orders = max_out_rate_limits(&mut client, verification).await;
1299
1300 let edit_start = Instant::now();
1301
1302 for i in 0..6 {
1304 let edit_request = edit_from_order(orders.get(i).unwrap());
1305 let _ = client.edit_order(&edit_request).await;
1306 }
1307
1308 for i in 0..12 {
1311 let edit_request = edit_from_order(orders.get(i).unwrap());
1312 let _ = client_err.edit_order(&edit_request).await;
1313 }
1314
1315 let edit_end = Instant::now();
1316 let edit_elapsed = edit_end - edit_start;
1317 println!("{:?}", edit_elapsed);
1318
1319 assert!(edit_elapsed > Duration::from_secs(12));
1320 assert!(edit_elapsed < Duration::from_secs(13));
1321 }
1322
1323 #[tokio::test]
1324 async fn test_cancel_order_max_penalty() {
1325 pause();
1326 let verification = Intermediate;
1327 let mut client = get_rate_limit_test_client(verification);
1328 let mut client_err = get_rate_limit_test_client_err(Pro);
1329
1330 let orders = max_out_rate_limits(&mut client, verification).await;
1331
1332 let edit_start = Instant::now();
1333
1334 for i in 0..4 {
1336 let cancel_request = cancel_from_order(orders.get(i).unwrap());
1337 let _ = client.cancel_order(&cancel_request).await;
1338 let _ = client_err.cancel_order(&cancel_request).await;
1339 }
1340
1341 for i in 0..12 {
1344 let cancel_request = cancel_from_order(orders.get(i).unwrap());
1345 let _ = client_err.cancel_order(&cancel_request).await;
1346 }
1347
1348 let edit_end = Instant::now();
1349 let edit_elapsed = edit_end - edit_start;
1350 println!("{:?}", edit_elapsed);
1351
1352 assert!(edit_elapsed > Duration::from_secs(14));
1353 assert!(edit_elapsed < Duration::from_secs(15));
1354 }
1355
1356 #[tokio::test]
1357 async fn test_cancel_order_batch_with_max_penalty() {
1358 pause();
1359 let verification = Intermediate;
1360 let mut client = get_rate_limit_test_client(verification);
1361 let mut client_err = get_rate_limit_test_client_err(Pro);
1362
1363 let mut orders = max_out_rate_limits(&mut client, verification).await;
1364
1365 let edit_start = Instant::now();
1366
1367 let mut order_ids = Vec::new();
1368 for i in 0..4 {
1369 let id = IntOrString::String(orders.get(i).unwrap().tx_id.first().unwrap().clone());
1370 order_ids.push(id);
1371 }
1372
1373 let user_ref_request = get_add_order_request_user_ref();
1374 orders.push(
1375 client
1376 .add_order(&user_ref_request)
1377 .await
1378 .unwrap()
1379 .result
1380 .unwrap(),
1381 );
1382 order_ids.push(IntOrString::Int(user_ref_request.user_ref.unwrap()));
1383
1384 let batch_cancel_request = CancelBatchOrdersRequest {
1385 orders: order_ids,
1386 client_order_ids: None,
1387 };
1388
1389 let _ = client.cancel_order_batch(&batch_cancel_request).await;
1392
1393 for _ in 0..5 {
1395 let _ = client_err.cancel_order_batch(&batch_cancel_request).await;
1396 }
1397
1398 let edit_end = Instant::now();
1399 let edit_elapsed = edit_end - edit_start;
1400 println!("{:?}", edit_elapsed);
1401
1402 assert!(edit_elapsed > Duration::from_secs(18));
1403 assert!(edit_elapsed < Duration::from_secs(19));
1404 }
1405
1406 async fn max_out_rate_limits(
1409 client: &mut TestRateLimitedClient,
1410 verification_tier: VerificationTier,
1411 ) -> Vec<AddOrder> {
1412 let start = Instant::now();
1413
1414 let request = get_add_order_request();
1415
1416 let n_orders = match verification_tier {
1417 Intermediate => 125,
1418 Pro => 180,
1419 };
1420
1421 let mut orders = Vec::new();
1423 for _ in 0..n_orders {
1424 let order = client.add_order(&request).await.unwrap().result.unwrap();
1425 orders.push(order);
1426 }
1427
1428 let end = Instant::now();
1429 let elapsed = end - start;
1430 println!("{:?}", elapsed);
1431
1432 assert!(elapsed >= Duration::from_secs(0));
1433 assert!(elapsed < Duration::from_millis(10));
1434 orders
1435 }
1436
1437 fn get_add_order_request() -> AddOrderRequest {
1438 let order_flags =
1439 OrderFlags::new(vec![OrderFlag::NoMarketPriceProtection, OrderFlag::Post]);
1440
1441 AddOrderRequest::builder(
1442 OrderType::Market,
1443 BuySell::Buy,
1444 dec!(5.0),
1445 "USDCUSD".to_string(),
1446 )
1447 .order_flags(order_flags)
1448 .price(dec!(0.90))
1449 .build()
1450 }
1451
1452 fn get_add_order_request_user_ref() -> AddOrderRequest {
1453 let order_flags =
1454 OrderFlags::new(vec![OrderFlag::NoMarketPriceProtection, OrderFlag::Post]);
1455
1456 AddOrderRequest::builder(
1457 OrderType::Market,
1458 BuySell::Buy,
1459 dec!(5.0),
1460 "USDCUSD".to_string(),
1461 )
1462 .user_ref(42)
1463 .order_flags(order_flags)
1464 .price(dec!(0.90))
1465 .build()
1466 }
1467
1468 fn get_batched_order_request(n_orders: u64) -> AddBatchedOrderRequest {
1469 let mut orders = Vec::new();
1470
1471 for _ in 0..n_orders {
1472 let order = BatchedOrderRequest::builder(OrderType::Limit, BuySell::Buy, dec!(5.1))
1473 .price(dec!(0.9))
1474 .start_time("0".to_string())
1475 .expire_time("+5".to_string())
1476 .build();
1477
1478 orders.push(order);
1479 }
1480
1481 AddBatchedOrderRequest::builder(orders, "USDCUSD".to_string()).build()
1482 }
1483
1484 fn edit_from_order(order: &AddOrder) -> EditOrderRequest {
1485 let edit_request = EditOrderRequest {
1486 user_ref: None,
1487 tx_id: order.tx_id.first().unwrap().clone(),
1488 volume: dec!(0),
1489 display_volume: None,
1490 pair: "".to_string(),
1491 price: None,
1492 price_2: None,
1493 order_flags: None,
1494 deadline: None,
1495 cancel_response: None,
1496 validate: None,
1497 };
1498 edit_request
1499 }
1500
1501 fn cancel_from_order(order: &AddOrder) -> CancelOrderRequest {
1502 CancelOrderRequest {
1503 tx_id: IntOrString::String(order.tx_id.first().unwrap().clone()),
1504 client_order_id: None,
1505 }
1506 }
1507
1508 #[tokio::test]
1509 async fn test_get_deposit_methods() {
1510 pause();
1511
1512 let request = DepositMethodsRequest::builder("ETH".to_string()).build();
1513
1514 test_rate_limited_endpoint!(get_deposit_methods, 24, 4, 5, Pro, &request);
1516 }
1517
1518 #[tokio::test]
1519 async fn test_get_deposit_addresses() {
1520 pause();
1521
1522 let request = DepositAddressesRequest::builder("BTC".to_string(), "Bitcoin".to_string())
1523 .is_new(true)
1524 .build();
1525
1526 test_rate_limited_endpoint!(get_deposit_addresses, 24, 8, 9, Intermediate, &request);
1528 }
1529
1530 #[tokio::test]
1531 async fn test_get_status_of_recent_deposits() {
1532 pause();
1533
1534 let request = StatusOfDepositWithdrawRequest::builder()
1535 .asset_class("currency".to_string())
1536 .build();
1537
1538 test_rate_limited_endpoint!(get_status_of_recent_deposits, 26, 6, 7, Pro, &request);
1540 }
1541
1542 #[tokio::test]
1543 async fn test_get_withdrawal_methods() {
1544 pause();
1545
1546 let request = WithdrawalMethodsRequest::builder()
1547 .asset_class("currency".to_string())
1548 .build();
1549
1550 test_rate_limited_endpoint!(get_withdrawal_methods, 26, 12, 13, Intermediate, &request);
1552 }
1553
1554 #[tokio::test]
1555 async fn test_get_withdrawal_addresses() {
1556 pause();
1557
1558 let request = WithdrawalAddressesRequest::builder()
1559 .asset_class("currency".to_string())
1560 .build();
1561
1562 test_rate_limited_endpoint!(get_withdrawal_addresses, 25, 5, 6, Pro, &request);
1564 }
1565
1566 #[tokio::test]
1567 async fn test_get_withdrawal_info() {
1568 pause();
1569
1570 let request = WithdrawalInfoRequest::builder(
1571 "XBT".to_string(),
1572 "Greenlisted Address".to_string(),
1573 dec!(0.1),
1574 )
1575 .build();
1576
1577 test_rate_limited_endpoint!(get_withdrawal_info, 25, 5, 6, Pro, &request);
1579 }
1580
1581 #[tokio::test]
1582 async fn test_withdraw_funds() {
1583 pause();
1584
1585 let request = WithdrawFundsRequest::builder(
1586 "XBT".to_string(),
1587 "Greenlisted Address".to_string(),
1588 dec!(0.1),
1589 )
1590 .max_fee(dec!(0.00001))
1591 .build();
1592
1593 test_rate_limited_endpoint!(withdraw_funds, 25, 10, 11, Intermediate, &request);
1595 }
1596
1597 #[tokio::test]
1598 async fn test_get_status_of_recent_withdrawals() {
1599 pause();
1600
1601 let request = StatusOfDepositWithdrawRequest::builder()
1602 .asset_class("currency".to_string())
1603 .build();
1604
1605 test_rate_limited_endpoint!(get_status_of_recent_withdrawals, 25, 5, 6, Pro, &request);
1607 }
1608
1609 #[tokio::test]
1610 async fn test_request_withdrawal_cancellation() {
1611 pause();
1612
1613 let request = WithdrawCancelRequest::builder("XBT".to_string(), "uuid".to_string()).build();
1614
1615 test_rate_limited_endpoint!(
1617 request_withdrawal_cancellation,
1618 27,
1619 14,
1620 15,
1621 Intermediate,
1622 &request
1623 );
1624 }
1625
1626 #[tokio::test]
1627 async fn test_request_wallet_transfer() {
1628 pause();
1629
1630 let request = WalletTransferRequest::builder(
1631 "XBT".to_string(),
1632 "Account One".to_string(),
1633 "Account Two".to_string(),
1634 dec!(0.25),
1635 )
1636 .build();
1637
1638 test_rate_limited_endpoint!(request_wallet_transfer, 27, 7, 8, Pro, &request);
1640 }
1641
1642 #[tokio::test]
1643 async fn test_create_sub_account() {
1644 pause();
1645
1646 let request =
1647 CreateSubAccountRequest::builder("username".to_string(), "user@mail.com".to_string())
1648 .build();
1649
1650 test_rate_limited_endpoint!(create_sub_account, 24, 4, 5, Pro, &request);
1652 }
1653
1654 #[tokio::test]
1655 async fn test_account_transfer() {
1656 pause();
1657
1658 let request = AccountTransferRequest::builder(
1659 "BTC".to_string(),
1660 dec!(1031.2008),
1661 "SourceAccount".to_string(),
1662 "DestAccount".to_string(),
1663 )
1664 .build();
1665
1666 test_rate_limited_endpoint!(account_transfer, 24, 8, 9, Intermediate, &request);
1668 }
1669
1670 #[tokio::test]
1671 async fn test_allocate_earn_funds() {
1672 pause();
1673
1674 let request =
1675 AllocateEarnFundsRequest::builder(dec!(10.123), "W38S2C-Y1E0R-DUFM2T".to_string())
1676 .build();
1677
1678 test_rate_limited_endpoint!(allocate_earn_funds, 24, 4, 5, Pro, &request);
1680 }
1681
1682 #[tokio::test]
1683 async fn test_deallocate_earn_funds() {
1684 pause();
1685
1686 let request =
1687 AllocateEarnFundsRequest::builder(dec!(10.123), "W38S2C-Y1E0R-DUFM2T".to_string())
1688 .build();
1689
1690 test_rate_limited_endpoint!(deallocate_earn_funds, 24, 8, 9, Intermediate, &request);
1692 }
1693
1694 #[tokio::test]
1695 async fn test_get_allocation_status() {
1696 pause();
1697
1698 let request =
1699 EarnAllocationStatusRequest::builder("W38S2C-Y1E0R-DUFM2T".to_string()).build();
1700
1701 test_rate_limited_endpoint!(get_earn_allocation_status, 24, 8, 9, Intermediate, &request);
1703 }
1704
1705 #[tokio::test]
1706 async fn test_get_deallocation_status() {
1707 pause();
1708
1709 let request =
1710 EarnAllocationStatusRequest::builder("W38S2C-Y1E0R-DUFM2T".to_string()).build();
1711
1712 test_rate_limited_endpoint!(
1714 get_earn_deallocation_status,
1715 24,
1716 8,
1717 9,
1718 Intermediate,
1719 &request
1720 );
1721 }
1722
1723 #[tokio::test]
1724 async fn test_list_earn_strategies() {
1725 pause();
1726
1727 let request = ListEarnStrategiesRequest::builder()
1728 .limit(64)
1729 .ascending(true)
1730 .build();
1731
1732 test_rate_limited_endpoint!(list_earn_strategies, 24, 4, 5, Pro, &request);
1734 }
1735
1736 #[tokio::test]
1737 async fn test_list_earn_allocations() {
1738 pause();
1739
1740 let request = ListEarnAllocationsRequest::builder()
1741 .ascending(true)
1742 .hide_zero_allocations(true)
1743 .build();
1744
1745 test_rate_limited_endpoint!(list_earn_allocations, 29, 18, 19, Intermediate, &request);
1747 }
1748
1749 #[tokio::test]
1750 async fn test_get_websockets_token() {
1751 pause();
1752
1753 test_rate_limited_endpoint!(get_websockets_token, 23, 3, 4, Pro);
1755 }
1756}