algotradelib 0.0.0.9000
C++ header-only library for algorithmic trading
Loading...
Searching...
No Matches
example-rate-limit-stats.cpp
1// Shows how a request budget is spent, and what the trading reserve protects.
2//
3// Entirely offline: it drives the pacer with its own clock and issues no broker
4// request, so it needs no credentials and touches no network. The point is to
5// make a budget's behaviour inspectable before a strategy depends on it.
6//
7// example-rate-limit-stats [burst] [per-second] [trading-reserve]
8
9#include <algotradelib/src/network/rate_limit.hpp>
10
11#include <chrono>
12#include <cstdlib>
13#include <exception>
14#include <iomanip>
15#include <iostream>
16#include <string>
17
18namespace {
19
24
26struct WalkClock {
27 using duration = std::chrono::nanoseconds;
28 using rep = duration::rep;
29 using period = duration::period;
30 using time_point = std::chrono::time_point<WalkClock, duration>;
31
33 static time_point now() { return time_point{}; }
34};
35
37
39std::chrono::nanoseconds ms(long long count) {
40 return std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::milliseconds(count));
41}
42
44long long as_ms(std::chrono::nanoseconds value) {
45 return std::chrono::duration_cast<std::chrono::milliseconds>(value).count();
46}
47
49int argument(int argc, char** argv, int index, int fallback) {
50 if (argc <= index) {
51 return fallback;
52 }
53 const int value = std::atoi(argv[index]);
54 return value >= 0 ? value : fallback;
55}
56
58void print_allowances(const RateLimitBudget& budget) {
59 std::cout << "budget: burst=" << budget.burst_ << " per_second=" << budget.per_second_
60 << " trading_reserve=" << budget.trading_reserve_ << '\n';
61 std::cout << " interval between admissions: " << as_ms(Bucket(budget).interval()) << "ms\n";
62 for (const auto group : {
63 RateLimitGroup::AUTH,
64 RateLimitGroup::TRADING,
65 RateLimitGroup::ACCOUNT,
66 RateLimitGroup::MARKET_DATA,
67 RateLimitGroup::REFERENCE,
68 }) {
69 std::cout << " " << std::left << std::setw(12) << rate_limit_group_name(group)
70 << " burst allowance " << usable_burst(budget, group) << '\n';
71 }
72}
73
75void print_reserve(const RateLimitBudget& budget) {
76 Bucket bucket(budget);
77 const auto now = WalkClock::now();
78 const int polling = usable_burst(budget, RateLimitGroup::MARKET_DATA);
79 const int trading = usable_burst(budget, RateLimitGroup::TRADING);
80
81 int spent = 0;
82 while (bucket.try_acquire(now, polling)) {
83 ++spent;
84 }
85
86 std::cout << "\na saturated polling loop spends " << spent << " request(s) immediately\n";
87 std::cout << " its next request waits " << as_ms(bucket.delay_until(now, polling)) << "ms\n";
88 std::cout << " a cancellation waits " << as_ms(bucket.delay_until(now, trading)) << "ms";
89 std::cout << " with " << bucket.available(now, trading) << " reserved slot(s) left\n";
90}
91
93void print_corrections(const RateLimitBudget& budget) {
94 std::cout << "\nreactions to a broker refusal\n";
95
97 shared.hold(std::chrono::seconds(1));
98 std::cout << " shared cap reached: allowance unchanged at " << shared.budget().per_second_
99 << "/s, admission held\n";
100
102 reported.shrink(2, 2, std::chrono::seconds(1));
103 std::cout << " broker reported its allowance: adopted " << reported.budget().per_second_
104 << "/s\n";
105
106 std::cout << " broker reported no allowance: allowance unchanged, admission held\n";
107 std::cout << " (KIS states none, so its budget stays as configured and its"
108 << " refusals are counted)\n";
109}
110
112void print_sustained(const RateLimitBudget& budget) {
113 Bucket bucket(budget);
114 const auto start = WalkClock::now();
115 const int polling = usable_burst(budget, RateLimitGroup::MARKET_DATA);
116
117 int admitted = 0;
118 for (long long step = 0; step < 1000; step += 5) {
119 if (bucket.try_acquire(start + ms(step), polling)) {
120 ++admitted;
121 }
122 }
123 std::cout << "\none second of polling as fast as allowed: " << admitted << " request(s)\n";
124}
125
126} // namespace
127
128int main(int argc, char** argv) {
129 try {
130 RateLimitBudget budget;
131 budget.burst_ = argument(argc, argv, 1, budget.burst_);
132 budget.per_second_ = argument(argc, argv, 2, budget.per_second_);
133 budget.trading_reserve_ = argument(argc, argv, 3, budget.trading_reserve_);
134
135 print_allowances(budget);
136 print_reserve(budget);
137 print_sustained(budget);
138 print_corrections(budget);
139 return 0;
140 } catch (const std::exception& exception) {
141 std::cerr << "example failed: " << exception.what() << '\n';
142 return 1;
143 }
144}
A rate limiter (the generic cell rate algorithm) that computes when a request may go; it neither wait...
The rate limiter of one connection (one app key): a RateLimitBucket on the steady clock,...
RateLimitGroup
The traffic class of a request, for rate limiting and queue priority. The broker adapter that builds ...
constexpr int usable_burst(const RateLimitBudget &budget, RateLimitGroup group) noexcept
Returns how much of the burst a traffic class may use.
constexpr std::string_view rate_limit_group_name(RateLimitGroup group) noexcept
Returns a lowercase name for a traffic class, such as "market_data", for logs.
void start(S &strategy, Ctx &context)
Calls strategy.on_start(context) if the strategy declares on_start; otherwise does nothing.
Definition concept.hpp:149
The request rate a connection may use.