Special Relativity in Financial Modeling 1.0.0
Lorentz transforms, spacetime classification, and geodesic price paths for quantitative finance
Loading...
Searching...
No Matches
beta_calculator.hpp
Go to the documentation of this file.
1#pragma once
2/**
3 * @file beta_calculator.hpp
4 * @brief Online market-velocity β calculator (FIX-N mode).
5 *
6 * Module: include/srfm/stream/
7 * Owner: AGT-10 (Builder) — 2026-03-01
8 *
9 * Responsibility
10 * --------------
11 * Compute the normalised market velocity β used by the Lorentz transform:
12 *
13 * log_return[i] = ln(close[i] / close[i-1])
14 * velocity_raw = mean(log_return[last N bars])
15 * β = clamp(velocity_raw / c_market, −BETA_MAX_SAFE, +BETA_MAX_SAFE)
16 *
17 * where N is the FIX parameter (default 3) and c_market is a calibration
18 * constant representing the "speed of light" for this market (typical daily
19 * log-return magnitude used as the upper bound).
20 *
21 * FIX-3 (N=3) means exactly 3 consecutive log-returns are averaged. This
22 * matches the original SRFM reference implementation's BetaCalculator.
23 *
24 * Guarantees
25 * ----------
26 * • Online: O(1) per tick.
27 * • Bounded: stores exactly N+1 close prices.
28 * • Always valid: β ∈ (−BETA_MAX_SAFE, +BETA_MAX_SAFE) after warm-up.
29 * • Returns β = 0 until N+1 prices have been seen (conservative).
30 * • noexcept: update() and beta() are noexcept.
31 *
32 * NOT Responsible For
33 * -------------------
34 * • Calibrating c_market (caller provides it at construction)
35 * • Cross-asset composition (compose_velocities in srfm::momentum)
36 */
37
38#include <array>
39#include <cmath>
40#include <cstddef>
41
42namespace srfm::stream {
43
44// ── Constants ─────────────────────────────────────────────────────────────────
45
46/// Maximum safe |β| — mirrors srfm::momentum::BETA_MAX_SAFE.
47inline constexpr double BETA_MAX_SAFE = 0.9999;
48
49/// Default market "speed of light" calibration constant.
50/// Represents a ~2 % per-tick log-return magnitude, which saturates β → 1.
51inline constexpr double DEFAULT_C_MARKET = 0.02;
52
53// ── BetaCalculator ────────────────────────────────────────────────────────────
54
55/**
56 * @brief Online β estimator with a sliding window of N log-returns.
57 *
58 * Template parameter N determines how many log-returns are averaged.
59 * FIX-3 mode (default) uses N=3.
60 *
61 * @tparam N Number of log-returns in the averaging window. Must be ≥ 1.
62 */
63template<std::size_t N = 3u>
65 static_assert(N >= 1u, "BetaCalculator: N must be at least 1");
66 static_assert(N <= 64u, "BetaCalculator: N must be at most 64 (compile-time array limit)");
67
68public:
69 /**
70 * @brief Construct with a market speed-of-light calibration constant.
71 *
72 * @param c_market The denominator used to normalise raw log-returns into β.
73 * Must be strictly positive and finite. Defaults to 0.02.
74 */
76 : c_market_{c_market > 0.0 && std::isfinite(c_market) ? c_market : DEFAULT_C_MARKET}
77 {}
78
79 // ── State mutation ─────────────────────────────────────────────────────────
80
81 /**
82 * @brief Ingest one new close price and update the rolling log-return buffer.
83 *
84 * @param close Current bar's close price. Must be positive and finite;
85 * non-positive / non-finite values are silently ignored
86 * (no state change) to protect the pipeline from bad data.
87 * @note noexcept.
88 */
89 void update(double close) noexcept {
90 if (!std::isfinite(close) || close <= 0.0) return;
91
92 if (prev_close_ > 0.0 && std::isfinite(prev_close_)) {
93 // Compute log-return and slide into circular buffer.
94 const double lr = std::log(close / prev_close_);
95 returns_[ret_pos_] = lr;
96 ret_pos_ = (ret_pos_ + 1u) % N;
97 if (ret_count_ < N) ++ret_count_;
98 }
99 prev_close_ = close;
100 ++close_count_;
101 }
102
103 // ── Query ──────────────────────────────────────────────────────────────────
104
105 /**
106 * @brief Current β estimate.
107 *
108 * Returns 0.0 until N log-returns have been accumulated.
109 * Guaranteed: |result| < BETA_MAX_SAFE.
110 *
111 * @note noexcept.
112 */
114 if (ret_count_ < N) return 0.0;
115
116 // Mean of the N log-returns in the circular buffer.
117 double sum = 0.0;
118 for (std::size_t i = 0u; i < N; ++i) sum += returns_[i];
119 const double velocity = sum / static_cast<double>(N);
120
121 // Normalise by c_market and clamp to valid range.
122 const double raw = velocity / c_market_;
123 if (raw >= BETA_MAX_SAFE) return BETA_MAX_SAFE - 1e-9;
124 if (raw <= -BETA_MAX_SAFE) return -BETA_MAX_SAFE + 1e-9;
125 return raw;
126 }
127
128 /**
129 * @brief Whether enough data has been seen to produce a reliable β.
130 *
131 * True once N+1 close prices have been ingested (N log-returns computed).
132 */
133 [[nodiscard]] bool warmed_up() const noexcept { return ret_count_ >= N; }
134
135 /// Number of close prices ingested so far.
136 [[nodiscard]] std::size_t close_count() const noexcept { return close_count_; }
137
138 /// Number of log-returns accumulated (capped at N).
139 [[nodiscard]] std::size_t ret_count() const noexcept { return ret_count_; }
140
141 /// Configured market speed-of-light constant.
142 [[nodiscard]] double c_market() const noexcept { return c_market_; }
143
144 // ── Reset ──────────────────────────────────────────────────────────────────
145
146 /**
147 * @brief Reset all state as if no ticks have been seen.
148 */
150 returns_.fill(0.0);
151 ret_pos_ = 0;
152 ret_count_ = 0;
153 prev_close_ = 0.0;
154 close_count_ = 0;
155 }
156
157private:
158 double c_market_; ///< Speed of light calibration.
159 std::array<double,N> returns_{}; ///< Circular buffer of log-returns.
160 std::size_t ret_pos_{0}; ///< Next write position in returns_.
161 std::size_t ret_count_{0}; ///< Returns accumulated (≤ N).
162 double prev_close_{0.0}; ///< Previous close for log-return.
163 std::size_t close_count_{0}; ///< Total close prices ingested.
164};
165
166/// Convenience alias for the default FIX-3 mode used throughout this pipeline.
168
169} // namespace srfm::stream
Online β estimator with a sliding window of N log-returns.
double beta() const noexcept
Current β estimate.
double c_market() const noexcept
Configured market speed-of-light constant.
void reset() noexcept
Reset all state as if no ticks have been seen.
BetaCalculator(double c_market=DEFAULT_C_MARKET) noexcept
Construct with a market speed-of-light calibration constant.
void update(double close) noexcept
Ingest one new close price and update the rolling log-return buffer.
bool warmed_up() const noexcept
Whether enough data has been seen to produce a reliable β.
std::size_t ret_count() const noexcept
Number of log-returns accumulated (capped at N).
std::size_t close_count() const noexcept
Number of close prices ingested so far.
constexpr double DEFAULT_C_MARKET
constexpr double BETA_MAX_SAFE
Maximum safe |β| — mirrors srfm::momentum::BETA_MAX_SAFE.