File size: 2,513 Bytes
f86fb3b | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | #pragma once
#include <cstdint>
#include <unordered_map>
#include "../core/types.hpp"
#include "../core/cache_line.hpp"
#include "../core/time_utils.hpp"
namespace quant::risk {
struct RiskLimits {
int64_t max_order_notional{1'000'000 * core::Price::SCALE}; // Default $1M max per order
double price_collar_pct{0.05}; // 5% fat-finger deviation collar
uint32_t max_orders_per_sec{5000}; // Leaky bucket rate limit
int64_t max_net_position{50000}; // Max absolute net shares position
};
struct LeakyBucket {
uint32_t tokens{0};
uint64_t last_refill_ns{0};
void refill(uint32_t rate_per_sec, uint64_t now_ns) noexcept {
if (last_refill_ns == 0) {
tokens = rate_per_sec;
last_refill_ns = now_ns;
return;
}
uint64_t elapsed_ns = now_ns - last_refill_ns;
if (elapsed_ns >= 1'000'000'000ULL) {
tokens = rate_per_sec;
last_refill_ns = now_ns;
} else {
uint64_t new_tokens = (elapsed_ns * rate_per_sec) / 1'000'000'000ULL;
if (new_tokens > 0) {
tokens = std::min(rate_per_sec, tokens + static_cast<uint32_t>(new_tokens));
last_refill_ns = now_ns;
}
}
}
bool consume() noexcept {
if (tokens > 0) {
--tokens;
return true;
}
return false;
}
};
class alignas(core::CACHELINE_SIZE) RiskEngine {
public:
explicit RiskEngine(RiskLimits limits = RiskLimits{}) : limits_(limits) {}
~RiskEngine() = default;
/**
* Inline Pre-Trade Risk Check.
* Evaluates in single-digit nanoseconds without locks.
*/
[[nodiscard]] core::RejectReason check_order(
core::ClientId client_id,
core::Side side,
core::Price price,
core::Quantity qty,
double current_mid_price,
uint64_t now_ns = 0
) noexcept;
/**
* Update client position upon trade execution.
*/
void on_fill(core::ClientId client_id, core::Side side, core::Quantity qty) noexcept;
void set_limits(RiskLimits limits) noexcept { limits_ = limits; }
[[nodiscard]] const RiskLimits& limits() const noexcept { return limits_; }
private:
RiskLimits limits_;
std::unordered_map<core::ClientId, LeakyBucket> rate_limiters_;
std::unordered_map<core::ClientId, int64_t> net_positions_;
};
} // namespace quant::risk
|