FastMM documentation
FastMM C++ API 0.6.0
Public headers, tier 1 and tier 2
Loading...
Searching...
No Matches
fastmm::venues::RateLimiter Class Reference

#include <fastmm/venues/rate_limiter.hpp>

Public Member Functions

 RateLimiter (double threshold=0.9) noexcept
 
void share_ip (std::shared_ptr< SharedRate > ip) noexcept
 The IP's weight, cooldown and hard stop live in ip from now on (null: this limiter's own).
 
bool add_weight_bucket (std::uint32_t limit, std::int64_t window_ns) noexcept
 Buckets are added at startup from exchangeInfo.rateLimits / the Bybit rate limit table.
 
bool add_order_bucket (std::uint32_t limit, std::int64_t window_ns) noexcept
 
void set_clock_offset (std::int64_t venue_minus_local_ns, std::int64_t now) noexcept
 The venue's clock minus the local (now) clock, ns: every window, the IP's included, rolls where the venue's does from now on.
 
void set_threshold (double t) noexcept
 
void set_order_threshold (double t) noexcept
 The share of each order-count limit (ORDERS buckets) can_send fills; the weight keeps threshold().
 
double order_threshold () const noexcept
 
double threshold () const noexcept
 The share of each limit can_send fills before it refuses.
 
std::int64_t cap_of (std::uint32_t limit) const noexcept
 The count a bucket of limit admits under that share: can_send refuses the order or request that would take used past it (RateBucket::would_exceed).
 
std::int64_t order_cap_of (std::uint32_t limit) const noexcept
 cap_of for an order-count bucket.
 
bool can_send (std::uint32_t weight, std::int64_t now, bool is_order=false, double share=1.0) noexcept
 True if a request of weight (and one order if is_order) fits under threshold * limit in every bucket and no cooldown/hard stop is active.
 
RateCheck check (std::uint32_t weight, std::int64_t now, bool is_order=false, double share=1.0) noexcept
 can_send, saying which limit refused.
 
void on_sent (std::uint32_t weight, std::int64_t now, bool is_order=false) noexcept
 Local accounting after a send.
 
void on_headers (std::int64_t used_weight, std::int64_t order_count, std::int64_t now, std::size_t weight_bucket=0, std::size_t order_bucket=0) noexcept
 The venue's counts from a response (RateBucket::calibrate).
 
void on_remaining (std::int64_t limit, std::int64_t remaining, std::int64_t now) noexcept
 Bybit style: remaining requests for this endpoint (X-Bapi-Limit-Status) and the limit.
 
void cooldown (std::int64_t retry_after_ns, std::int64_t now) noexcept
 429 / -1003 / 10006: block sends until now + retry_after, or longer when the last pause ended less than kStreakWindowNs ago (the streak).
 
std::int64_t cooldown_until () const noexcept
 
std::uint32_t streak () const noexcept
 Pauses in the current streak after the first.
 
bool in_cooldown (std::int64_t now) const noexcept
 
void hard_stop () noexcept
 418: the IP is banned; stop REST entirely until an operator clears it.
 
void clear_hard_stop () noexcept
 
bool hard_stopped () const noexcept
 
std::uint64_t cooldowns () const noexcept
 
std::optional< RateBucket > weight_bucket (std::size_t i) const noexcept
 Copies of the i-th bucket (insertion order), empty past the last; a shared IP's is copied under its lock, since the other accounts' threads write it.
 
std::optional< RateBucket > order_bucket (std::size_t i) const noexcept
 

Static Public Attributes

static constexpr std::size_t kMaxBuckets = 4
 
static constexpr double kBulkShare = 0.5
 The part of each weight window a bulk request may fill (can_send's share): with 170 symbols' snapshots and history queries sent as fast as they fit, the other half of the window still takes a cancel or a REST order.
 
static constexpr std::int64_t kStreakWindowNs = 60'000'000'000
 A pause asked (429) within this long after the last one ended is the same incident going on: each such pause waits kBackoffBaseNs << n on top of Retry-After, up to kBackoffMaxNs.
 
static constexpr std::int64_t kBackoffBaseNs = 1'000'000'000
 
static constexpr std::int64_t kBackoffMaxNs = 120'000'000'000
 
static constexpr double kBulkPaceFloor = 0.2
 The part of kBulkShare this limiter's own weight in a window may fill before a bulk request grows with the time into the window, from this part at its start to all of it at its end: a backlog of history queries waiting for room no longer spends the share the moment the window opens, while the orders are busiest there.
 
static constexpr std::int64_t kHeaderLagNs = 2'000'000'000
 How late a venue's count (on_headers) may be: what was sent in this long before it arrived may be missing from it.
 

Constructor & Destructor Documentation

◆ RateLimiter()

fastmm::venues::RateLimiter::RateLimiter ( double  threshold = 0.9)
inlineexplicitnoexcept

Member Function Documentation

◆ share_ip()

void fastmm::venues::RateLimiter::share_ip ( std::shared_ptr< SharedRate >  ip)
inlinenoexcept

The IP's weight, cooldown and hard stop live in ip from now on (null: this limiter's own).

◆ add_weight_bucket()

bool fastmm::venues::RateLimiter::add_weight_bucket ( std::uint32_t  limit,
std::int64_t  window_ns 
)
inlinenoexcept

Buckets are added at startup from exchangeInfo.rateLimits / the Bybit rate limit table.

◆ add_order_bucket()

bool fastmm::venues::RateLimiter::add_order_bucket ( std::uint32_t  limit,
std::int64_t  window_ns 
)
inlinenoexcept

◆ set_clock_offset()

void fastmm::venues::RateLimiter::set_clock_offset ( std::int64_t  venue_minus_local_ns,
std::int64_t  now 
)
inlinenoexcept

The venue's clock minus the local (now) clock, ns: every window, the IP's included, rolls where the venue's does from now on.

0 (the default): windows of the local clock.

◆ set_threshold()

void fastmm::venues::RateLimiter::set_threshold ( double  t)
inlinenoexcept

◆ set_order_threshold()

void fastmm::venues::RateLimiter::set_order_threshold ( double  t)
inlinenoexcept

The share of each order-count limit (ORDERS buckets) can_send fills; the weight keeps threshold().

An order count past its limit costs a refused order, the weight a ban.

◆ order_threshold()

double fastmm::venues::RateLimiter::order_threshold ( ) const
inlinenoexcept

◆ threshold()

double fastmm::venues::RateLimiter::threshold ( ) const
inlinenoexcept

The share of each limit can_send fills before it refuses.

◆ cap_of()

std::int64_t fastmm::venues::RateLimiter::cap_of ( std::uint32_t  limit) const
inlinenoexcept

The count a bucket of limit admits under that share: can_send refuses the order or request that would take used past it (RateBucket::would_exceed).

◆ order_cap_of()

std::int64_t fastmm::venues::RateLimiter::order_cap_of ( std::uint32_t  limit) const
inlinenoexcept

cap_of for an order-count bucket.

◆ can_send()

bool fastmm::venues::RateLimiter::can_send ( std::uint32_t  weight,
std::int64_t  now,
bool  is_order = false,
double  share = 1.0 
)
inlinenoexcept

True if a request of weight (and one order if is_order) fits under threshold * limit in every bucket and no cooldown/hard stop is active.

share < 1 (kBulkShare) leaves that part of each weight window to other requests.

◆ check()

RateCheck fastmm::venues::RateLimiter::check ( std::uint32_t  weight,
std::int64_t  now,
bool  is_order = false,
double  share = 1.0 
)
inlinenoexcept

can_send, saying which limit refused.

◆ on_sent()

void fastmm::venues::RateLimiter::on_sent ( std::uint32_t  weight,
std::int64_t  now,
bool  is_order = false 
)
inlinenoexcept

Local accounting after a send.

◆ on_headers()

void fastmm::venues::RateLimiter::on_headers ( std::int64_t  used_weight,
std::int64_t  order_count,
std::int64_t  now,
std::size_t  weight_bucket = 0,
std::size_t  order_bucket = 0 
)
inlinenoexcept

The venue's counts from a response (RateBucket::calibrate).

bucket selects the window (index in insertion order); negative values are ignored (header absent).

◆ on_remaining()

void fastmm::venues::RateLimiter::on_remaining ( std::int64_t  limit,
std::int64_t  remaining,
std::int64_t  now 
)
inlinenoexcept

Bybit style: remaining requests for this endpoint (X-Bapi-Limit-Status) and the limit.

◆ cooldown()

void fastmm::venues::RateLimiter::cooldown ( std::int64_t  retry_after_ns,
std::int64_t  now 
)
inlinenoexcept

429 / -1003 / 10006: block sends until now + retry_after, or longer when the last pause ended less than kStreakWindowNs ago (the streak).

A pause asked while one is on only extends it: the answers to the requests already in flight are one incident.

< this account's count of the pauses it was asked for

◆ cooldown_until()

std::int64_t fastmm::venues::RateLimiter::cooldown_until ( ) const
inlinenoexcept

◆ streak()

std::uint32_t fastmm::venues::RateLimiter::streak ( ) const
inlinenoexcept

Pauses in the current streak after the first.

◆ in_cooldown()

bool fastmm::venues::RateLimiter::in_cooldown ( std::int64_t  now) const
inlinenoexcept

◆ hard_stop()

void fastmm::venues::RateLimiter::hard_stop ( )
inlinenoexcept

418: the IP is banned; stop REST entirely until an operator clears it.

◆ clear_hard_stop()

void fastmm::venues::RateLimiter::clear_hard_stop ( )
inlinenoexcept

◆ hard_stopped()

bool fastmm::venues::RateLimiter::hard_stopped ( ) const
inlinenoexcept

◆ cooldowns()

std::uint64_t fastmm::venues::RateLimiter::cooldowns ( ) const
inlinenoexcept

◆ weight_bucket()

std::optional< RateBucket > fastmm::venues::RateLimiter::weight_bucket ( std::size_t  i) const
inlinenoexcept

Copies of the i-th bucket (insertion order), empty past the last; a shared IP's is copied under its lock, since the other accounts' threads write it.

◆ order_bucket()

std::optional< RateBucket > fastmm::venues::RateLimiter::order_bucket ( std::size_t  i) const
inlinenoexcept

Member Data Documentation

◆ kMaxBuckets

constexpr std::size_t fastmm::venues::RateLimiter::kMaxBuckets = 4
staticconstexpr

◆ kBulkShare

constexpr double fastmm::venues::RateLimiter::kBulkShare = 0.5
staticconstexpr

The part of each weight window a bulk request may fill (can_send's share): with 170 symbols' snapshots and history queries sent as fast as they fit, the other half of the window still takes a cancel or a REST order.

◆ kStreakWindowNs

constexpr std::int64_t fastmm::venues::RateLimiter::kStreakWindowNs = 60'000'000'000
staticconstexpr

A pause asked (429) within this long after the last one ended is the same incident going on: each such pause waits kBackoffBaseNs << n on top of Retry-After, up to kBackoffMaxNs.

The venue's Retry-After names a second; a client back at once earns the next 429, then the ban.

◆ kBackoffBaseNs

constexpr std::int64_t fastmm::venues::RateLimiter::kBackoffBaseNs = 1'000'000'000
staticconstexpr

◆ kBackoffMaxNs

constexpr std::int64_t fastmm::venues::RateLimiter::kBackoffMaxNs = 120'000'000'000
staticconstexpr

◆ kBulkPaceFloor

constexpr double fastmm::venues::RateLimiter::kBulkPaceFloor = 0.2
staticconstexpr

The part of kBulkShare this limiter's own weight in a window may fill before a bulk request grows with the time into the window, from this part at its start to all of it at its end: a backlog of history queries waiting for room no longer spends the share the moment the window opens, while the orders are busiest there.

◆ kHeaderLagNs

constexpr std::int64_t fastmm::venues::RateLimiter::kHeaderLagNs = 2'000'000'000
staticconstexpr

How late a venue's count (on_headers) may be: what was sent in this long before it arrived may be missing from it.