xapi_binance/common/
ratelimiter.rs

1use crate::data::enums::ratelimit::{BnRateLimit, BnRateLimitInterval, BnRateLimitType};
2use governor::{DefaultDirectRateLimiter, InsufficientCapacity, Quota};
3use std::{collections::HashMap, num::NonZero, time::Duration};
4use xapi_shared::ratelimiter::SharedRatelimiterTrait;
5
6pub struct BnRatelimiter {
7    limiters: HashMap<BnRateLimitType, Vec<DefaultDirectRateLimiter>>,
8}
9
10#[async_trait::async_trait]
11impl SharedRatelimiterTrait<BnRateLimitType> for BnRatelimiter {
12    async fn limit_on(
13        &self,
14        key: &BnRateLimitType,
15        value: NonZero<u32>,
16    ) -> Result<(), InsufficientCapacity> {
17        if let Some(limiters) = self.limiters.get(key) {
18            for limiter in limiters {
19                limiter
20                    .until_n_ready(value)
21                    .await
22                    .inspect_err(|err| tracing::error!("bn rate limiter error: {err}"))?
23            }
24        }
25
26        Ok(())
27    }
28}
29
30impl Default for BnRatelimiter {
31    fn default() -> Self {
32        let limits = vec![
33            BnRateLimit {
34                rate_limit_type: BnRateLimitType::RequestWeight,
35                interval: BnRateLimitInterval::Minute,
36                interval_num: 1,
37                limit: 6000,
38            },
39            BnRateLimit {
40                rate_limit_type: BnRateLimitType::Orders,
41                interval: BnRateLimitInterval::Second,
42                interval_num: 10,
43                limit: 100,
44            },
45            BnRateLimit {
46                rate_limit_type: BnRateLimitType::Orders,
47                interval: BnRateLimitInterval::Day,
48                interval_num: 1,
49                limit: 200_000,
50            },
51            BnRateLimit {
52                rate_limit_type: BnRateLimitType::RawRequests,
53                interval: BnRateLimitInterval::Minute,
54                interval_num: 5,
55                limit: 61_000,
56            },
57        ];
58
59        Self::new(&limits)
60    }
61}
62
63impl BnRatelimiter {
64    pub fn new(rules: &Vec<BnRateLimit>) -> Self {
65        let mut limiters = HashMap::new();
66
67        for rule in rules {
68            if rule.limit == 0 {
69                tracing::warn!("skipping rate limit rule with non-positive limit: {rule:?}");
70                continue;
71            }
72
73            let limit = NonZero::new(rule.limit).unwrap();
74
75            let quota = match rule.interval {
76                BnRateLimitInterval::Second => Quota::per_second(limit),
77                BnRateLimitInterval::Minute => Quota::per_minute(limit),
78                BnRateLimitInterval::Day => Quota::with_period(Duration::from_secs(60 * 60 * 24))
79                    .unwrap()
80                    .allow_burst(limit),
81            };
82
83            let limiter = DefaultDirectRateLimiter::direct(quota);
84
85            limiters
86                .entry(rule.rate_limit_type)
87                .or_insert_with(Vec::new)
88                .push(limiter);
89        }
90
91        Self { limiters }
92    }
93}