FastMM documentation
FastMM C++ API 0.6.0
Public headers, tier 1 and tier 2
Loading...
Searching...
No Matches
rate_limiter.hpp File Reference

RateLimiter (6.4/6.5): client-side accounting of venue rate limits so we refuse to send before the venue does. More...

#include "fastmm/core/spin_mutex.hpp"
#include <algorithm>
#include <array>
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <functional>
#include <map>
#include <memory>
#include <mutex>
#include <optional>
#include <string>
#include <string_view>

Go to the source code of this file.

Classes

struct  fastmm::venues::RateBucket
 
class  fastmm::venues::RecentSpend
 What was sent in the last couple of seconds (RateLimiter::kHeaderLagNs), in 125 ms slots. More...
 
struct  fastmm::venues::RateCheck
 
class  fastmm::venues::RateLimiter
 
struct  fastmm::venues::SharedRate
 One IP's request weight, shared by every account that sends from it (RateLimiter::share_ip). More...
 

Namespaces

namespace  fastmm
 
namespace  fastmm::venues
 

Enumerations

enum class  fastmm::venues::RateRefusal : std::uint8_t { fastmm::venues::None , fastmm::venues::Paused , fastmm::venues::Weight , fastmm::venues::Orders }
 Why can_send refused (RateLimiter::check): the IP or account is paused (429 cooldown, 418), a weight window is at its share, or an order-count window is (RateCheck::window_ns says which). More...
 

Functions

std::string fastmm::venues::ip_weight_key (std::string_view kind, std::string_view rest_url, std::string_view source)
 The key of one IP's weight at one venue: the connector kind, its REST host and the address its connections leave from (net::source_label; empty: the host's default), so accounts sending from different addresses count apart and those sending from the same one count together.
 
std::shared_ptr< SharedRate > fastmm::venues::shared_rate (std::string_view key)
 The SharedRate of key (ip_weight_key), made on first use; process-wide.
 
template<class Sleep >
void fastmm::venues::wait_for_weight (RateLimiter &r, std::uint32_t weight, std::int64_t now_ns, std::int64_t max_wait_ns, Sleep &&sleep_ns) noexcept
 A blocking start-up request (reference data, account settings, one per symbol) waits for its weight to fit the bulk share instead of spending what the connections about to open will need; at most max_wait_ns, since nothing trades yet and a window is a minute.
 

Detailed Description

RateLimiter (6.4/6.5): client-side accounting of venue rate limits so we refuse to send before the venue does.

Two kinds of buckets:

  • weight buckets - Binance REQUEST_WEIGHT per interval (X-MBX-USED-WEIGHT-1M), Bybit per-endpoint X-Bapi-Limit / X-Bapi-Limit-Status;
  • order-count buckets - Binance ORDERS per 10 s / per day (X-MBX-ORDER-COUNT-10S), Bybit create/cancel per second per UID. The venue's own counts (response headers, WS API rateLimits) correct the local estimate (on_headers): raised to the venue's count, and lowered to it plus what was sent in the last kHeaderLagNs, which the count may not include yet. Windows roll where the venue's do, on its clock (set_clock_offset: Binance's minute and 10 s windows start on the UTC minute and second). A cooldown (429 Retry-After, -1003, 10006) blocks every send until it expires, and a second one soon after the first waits longer each time; a hard stop (418 IP ban) blocks until explicitly cleared. Bulk requests (a start-up's depth snapshots and history queries, one per symbol) check against kBulkShare of the weight, and what this limiter sent in the window (every account of the process, on a shared IP) against that share paced over it (kBulkPaceFloor), so the rest of the window stays with the orders and the bulk does not pile up at the start of each window. The pace leaves out what other processes on the IP spent: a second process early in a minute (a warm standby, a restart) would otherwise find the paced share spent by the first and wait seconds for its first depth snapshot. Several accounts behind one IP (an account pool) share the IP's weight: share_ip() points the weight buckets, the cooldown and the hard stop at one SharedRate per host (shared_rate()), so a 429 seen by one account pauses them all; the order-count buckets stay the account's.