xapi-binance 0.0.1

Binance API client
Documentation
use crate::data::enums::ratelimit::{BnRateLimit, BnRateLimitInterval, BnRateLimitType};
use governor::{DefaultDirectRateLimiter, InsufficientCapacity, Quota};
use std::{collections::HashMap, num::NonZero, time::Duration};
use xapi_shared::ratelimiter::SharedRatelimiterTrait;

pub struct BnRatelimiter {
    limiters: HashMap<BnRateLimitType, Vec<DefaultDirectRateLimiter>>,
}

#[async_trait::async_trait]
impl SharedRatelimiterTrait<BnRateLimitType> for BnRatelimiter {
    async fn limit_on(
        &self,
        key: &BnRateLimitType,
        value: NonZero<u32>,
    ) -> Result<(), InsufficientCapacity> {
        if let Some(limiters) = self.limiters.get(key) {
            for limiter in limiters {
                limiter
                    .until_n_ready(value)
                    .await
                    .inspect_err(|err| tracing::error!("bn rate limiter error: {err}"))?
            }
        }

        Ok(())
    }
}

impl Default for BnRatelimiter {
    fn default() -> Self {
        let limits = vec![
            BnRateLimit {
                rate_limit_type: BnRateLimitType::RequestWeight,
                interval: BnRateLimitInterval::Minute,
                interval_num: 1,
                limit: 6000,
            },
            BnRateLimit {
                rate_limit_type: BnRateLimitType::Orders,
                interval: BnRateLimitInterval::Second,
                interval_num: 10,
                limit: 100,
            },
            BnRateLimit {
                rate_limit_type: BnRateLimitType::Orders,
                interval: BnRateLimitInterval::Day,
                interval_num: 1,
                limit: 200_000,
            },
            BnRateLimit {
                rate_limit_type: BnRateLimitType::RawRequests,
                interval: BnRateLimitInterval::Minute,
                interval_num: 5,
                limit: 61_000,
            },
        ];

        Self::new(&limits)
    }
}

impl BnRatelimiter {
    pub fn new(rules: &Vec<BnRateLimit>) -> Self {
        let mut limiters = HashMap::new();

        for rule in rules {
            if rule.limit == 0 {
                tracing::warn!("skipping rate limit rule with non-positive limit: {rule:?}");
                continue;
            }

            let limit = NonZero::new(rule.limit).unwrap();

            let quota = match rule.interval {
                BnRateLimitInterval::Second => Quota::per_second(limit),
                BnRateLimitInterval::Minute => Quota::per_minute(limit),
                BnRateLimitInterval::Day => Quota::with_period(Duration::from_secs(60 * 60 * 24))
                    .unwrap()
                    .allow_burst(limit),
            };

            let limiter = DefaultDirectRateLimiter::direct(quota);

            limiters
                .entry(rule.rate_limit_type)
                .or_insert_with(Vec::new)
                .push(limiter);
        }

        Self { limiters }
    }
}