Special Relativity in Financial Modeling 1.0.0
Lorentz transforms, spacetime classification, and geodesic price paths for quantitative finance
Loading...
Searching...
No Matches
normalizer.cpp
Go to the documentation of this file.
1/// @file src/manifold/normalizer.cpp
2/// @brief CoordinateNormalizer — rolling z-score normalization for SpacetimeEvent.
3///
4/// Maintains three independent rolling deques (price, volume, momentum).
5/// Each normalize() call:
6/// 1. Appends the new coordinate values to their respective deques
7/// 2. Trims each deque to the configured window size
8/// 3. Computes rolling mean and sample stddev for each coordinate
9/// 4. Returns the z-scored event (time passes through unchanged)
10
11#include "srfm/normalizer.hpp"
12
13#include <algorithm>
14#include <cassert>
15#include <cmath>
16#include <numeric>
17#include <optional>
18
19namespace srfm {
20
21// ─── Constructor ──────────────────────────────────────────────────────────────
22
24 std::size_t min_samples) noexcept
25 : window_(window < 1 ? 1 : window)
26 , min_samples_(min_samples == 0
27 ? std::max(window_, DEFAULT_MIN_SAMPLES)
28 : min_samples) {
29 assert(window > 0 && "CoordinateNormalizer: window_size must be > 0");
30}
31
32// ─── push ─────────────────────────────────────────────────────────────────────
33
34void CoordinateNormalizer::push(std::deque<double>& buf,
35 double value,
36 std::size_t max_size) noexcept {
37 buf.push_back(value);
38 if (buf.size() > max_size) {
39 buf.pop_front();
40 }
41}
42
43// ─── rolling_mean ─────────────────────────────────────────────────────────────
44
45double CoordinateNormalizer::rolling_mean(const std::deque<double>& buf) noexcept {
46 // Precondition: buf is non-empty.
47 double sum = 0.0;
48 for (double v : buf) {
49 sum += v;
50 }
51 return sum / static_cast<double>(buf.size());
52}
53
54// ─── rolling_stddev ───────────────────────────────────────────────────────────
55
56double CoordinateNormalizer::rolling_stddev(const std::deque<double>& buf,
57 double mean) noexcept {
58 if (buf.size() < 2) {
59 // Cannot compute sample stddev from a single observation.
60 return 0.0;
61 }
62 double sq_sum = 0.0;
63 for (double v : buf) {
64 const double d = v - mean;
65 sq_sum += d * d;
66 }
67 // Bessel-corrected (n-1) sample standard deviation.
68 return std::sqrt(sq_sum / static_cast<double>(buf.size() - 1));
69}
70
71// ─── zscore ───────────────────────────────────────────────────────────────────
72
73double CoordinateNormalizer::zscore(double value,
74 const std::deque<double>& buf) noexcept {
75 if (buf.empty()) {
76 return 0.0;
77 }
78 const double m = rolling_mean(buf);
79 const double s = rolling_stddev(buf, m);
80 if (s < FLAT_STDDEV_THRESHOLD) {
81 // Flat series — no variance to normalize against.
82 return 0.0;
83 }
84 return (value - m) / s;
85}
86
87// ─── normalize ────────────────────────────────────────────────────────────────
88
89std::optional<manifold::SpacetimeEvent>
91 // Update each rolling buffer with the new bar's spatial coordinates.
92 push(price_buf_, raw.price, window_);
93 push(volume_buf_, raw.volume, window_);
94 push(momentum_buf_, raw.momentum, window_);
95 ++total_;
96
97 // Warm-up guard: return std::nullopt until min_samples_ observations have
98 // been accumulated. Callers should check warmed_up() before using the
99 // output of normalize() for trading decisions.
100 if (!warmed_up()) {
101 return std::nullopt;
102 }
103
104 // Z-score each spatial coordinate using the updated window.
105 // Time coordinate is always passed through unchanged.
107 .time = raw.time,
108 .price = zscore(raw.price, price_buf_),
109 .volume = zscore(raw.volume, volume_buf_),
110 .momentum = zscore(raw.momentum, momentum_buf_),
111 };
112}
113
114// ─── size / window_size / reset ───────────────────────────────────────────────
115
116std::size_t CoordinateNormalizer::size() const noexcept {
117 // All three deques are always the same size; any one is representative.
118 return price_buf_.size();
119}
120
121std::size_t CoordinateNormalizer::window_size() const noexcept {
122 return window_;
123}
124
125std::size_t CoordinateNormalizer::total_samples() const noexcept {
126 return total_;
127}
128
129bool CoordinateNormalizer::warmed_up() const noexcept {
130 return total_ >= min_samples_;
131}
132
134 price_buf_.clear();
135 volume_buf_.clear();
136 momentum_buf_.clear();
137 total_ = 0;
138}
139
140} // namespace srfm
std::size_t total_samples() const noexcept
Total number of samples seen so far (does not saturate at window_size()).
CoordinateNormalizer(std::size_t window=20, std::size_t min_samples=0) noexcept
bool warmed_up() const noexcept
std::size_t size() const noexcept
Number of samples currently in the rolling window (≤ window_size()).
void reset() noexcept
Reset the normalizer, clearing all buffered observations.
std::size_t window_size() const noexcept
Configured maximum window size.
std::optional< manifold::SpacetimeEvent > normalize(const manifold::SpacetimeEvent &raw) noexcept
CoordinateNormalizer — rolling z-score normalizer for SpacetimeEvent.
A point in 4D spacetime (t, x, y, z).
double time
Market time coordinate (bar index or timestamp)
Definition manifold.hpp:52