Special Relativity in Financial Modeling 1.0.0
Lorentz transforms, spacetime classification, and geodesic price paths for quantitative finance
Loading...
Searching...
No Matches
multi_asset.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file include/srfm/multi_asset.hpp
4/// @brief Multi-asset spacetime extension for the SRFM library.
5///
6/// Extends the single-asset spacetime framework (SpacetimeEvent, SpacetimeInterval)
7/// to handle N correlated financial assets simultaneously by:
8///
9/// 1. Embedding a portfolio snapshot as an (N+1)-dimensional spacetime event.
10/// 2. Computing the N-dimensional Lorentzian interval using a rolling correlation
11/// matrix as the spatial metric block.
12/// 3. Applying simultaneous Lorentz transforms across all correlated price series.
13/// 4. Computing portfolio geodesics that represent the optimal portfolio path
14/// under the given metric geometry.
15///
16/// ## Physical Interpretation
17/// In multi-asset spacetime, the metric tensor couples price dimensions through
18/// the pairwise correlations ρ_ij. A TIMELIKE interval in the multi-asset sense
19/// implies that the *portfolio* as a whole moved causally — the joint price motion
20/// was consistent with sub-light correlated propagation. A SPACELIKE interval
21/// implies decorrelated (stochastic) joint motion.
22///
23/// ## Guarantees
24/// - All fallible operations return std::optional (no exceptions).
25/// - All const methods are thread-safe.
26/// - No raw pointers in the public API.
27/// - Eigen3 is used for all linear algebra.
28
29#include "srfm/types.hpp"
30#include "srfm/constants.hpp"
33
34#include <Eigen/Dense>
35#include <optional>
36#include <span>
37#include <string>
38#include <vector>
39
40namespace srfm::multi_asset {
41
42// ---------------------------------------------------------------------------
43// MultiAssetEvent
44// ---------------------------------------------------------------------------
45
46/// @brief A snapshot of N correlated financial assets at a point in time.
47///
48/// Represents a single row of a multi-asset OHLCV dataset embedded as a
49/// spacetime event in the (N+1)-dimensional financial spacetime manifold.
51 std::vector<std::string> symbols; ///< Asset ticker symbols (for labelling).
52 std::vector<double> prices; ///< Mid-prices for each asset.
53 std::vector<double> volumes; ///< Traded volumes for each asset.
54 int64_t timestamp; ///< Unix epoch milliseconds.
55
56 /// @brief Return the number of assets represented.
57 [[nodiscard]] std::size_t n_assets() const noexcept { return prices.size(); }
58
59 /// @brief Validate that all vectors have equal length.
60 [[nodiscard]] bool is_valid() const noexcept {
61 return !prices.empty() &&
62 prices.size() == volumes.size() &&
63 (symbols.empty() || symbols.size() == prices.size());
64 }
65};
66
67// ---------------------------------------------------------------------------
68// MultiAssetInterval
69// ---------------------------------------------------------------------------
70
71/// @brief N-dimensional spacetime interval between two MultiAssetEvents.
72///
73/// The metric tensor g_μν is:
74///
75/// g_00 = −c_market² (time-time, negative signature)
76/// g_0i = 0 (no time-space cross-terms)
77/// g_ij = Σ_ij (price-price block: correlation-scaled covariance)
78///
79/// where Σ_ij = ρ_ij · σ_i · σ_j is the rolling covariance matrix.
80///
81/// ## Classification
82/// ds² < -LIGHTLIKE_EPS → TIMELIKE (causal, correlated joint motion)
83/// |ds²| ≤ LIGHTLIKE_EPS → LIGHTLIKE (critical boundary)
84/// ds² > +LIGHTLIKE_EPS → SPACELIKE (stochastic, decorrelated motion)
86public:
88
89 /// @brief Threshold for LIGHTLIKE classification.
91
92 /// @brief Compute ds² between two N-asset spacetime events.
93 ///
94 /// @param a First event (earlier in time).
95 /// @param b Second event (later in time).
96 /// @param metric The (N+1)×(N+1) Lorentzian metric tensor g_μν.
97 /// @return ds² value, or std::nullopt if events have different asset counts
98 /// or the metric dimensions are inconsistent.
99 [[nodiscard]] static std::optional<double>
100 compute(const MultiAssetEvent& a,
101 const MultiAssetEvent& b,
102 const Eigen::MatrixXd& metric) noexcept;
103
104 /// @brief Classify a pre-computed ds² value.
105 [[nodiscard]] static IntervalType classify(double ds2) noexcept;
106
107 /// @brief Compute and classify in one call.
108 ///
109 /// @return Pair (ds², IntervalType), or nullopt on failure.
110 [[nodiscard]] static std::optional<std::pair<double, IntervalType>>
112 const MultiAssetEvent& b,
113 const Eigen::MatrixXd& metric) noexcept;
114};
115
116// ---------------------------------------------------------------------------
117// CorrelationMetric
118// ---------------------------------------------------------------------------
119
120/// @brief Builds the Lorentzian metric tensor from a rolling correlation matrix.
121///
122/// Maintains a sliding window of N-asset price observations and computes the
123/// (N+1)×(N+1) metric tensor g_μν with:
124/// - g_00 = -c_market²
125/// - g_ij = rolling_correlation[i,j] × sqrt(rolling_variance[i] × rolling_variance[j])
126///
127/// The Cholesky decomposition of the spatial block is checked on every update;
128/// if the covariance matrix is not positive-definite (ill-conditioned or
129/// singular correlation), the metric is regularised by adding
130/// `regularisation_eps × I` to the spatial block.
132public:
133 /// @brief Construction parameters.
134 struct Config {
135 std::size_t window_size{60}; ///< Rolling window (number of bars).
136 double c_market{1.0}; ///< Market speed-of-light.
137 double regularisation_eps{1e-6}; ///< Ridge regularisation for ill-conditioned Σ.
138 };
139
140 /// @brief Construct with given configuration.
141 explicit CorrelationMetric(std::size_t n_assets, Config cfg = {});
142
143 /// @brief Add a new bar observation and update the metric.
144 ///
145 /// @param prices New price observations for each asset.
146 /// @return Updated metric tensor, or std::nullopt if fewer than 2 observations.
147 [[nodiscard]] std::optional<Eigen::MatrixXd>
148 update(const std::vector<double>& prices);
149
150 /// @brief Return the current metric tensor (last computed).
151 [[nodiscard]] std::optional<Eigen::MatrixXd> current_metric() const noexcept;
152
153 /// @brief Return the rolling correlation matrix (spatial block only).
154 [[nodiscard]] std::optional<Eigen::MatrixXd> correlation_matrix() const noexcept;
155
156 /// @brief Return the number of assets.
157 [[nodiscard]] std::size_t n_assets() const noexcept { return n_assets_; }
158
159 /// @brief Return the number of observations seen so far.
160 [[nodiscard]] std::size_t observation_count() const noexcept { return obs_count_; }
161
162 /// @brief Return the configuration.
163 [[nodiscard]] const Config& config() const noexcept { return cfg_; }
164
165 /// @brief Reset the window (clears all observations).
166 void reset() noexcept;
167
168private:
169 std::size_t n_assets_;
170 Config cfg_;
171 std::size_t obs_count_{0};
172
173 // Circular buffer of log-return observations.
174 std::vector<Eigen::VectorXd> log_returns_window_;
175 std::size_t window_head_{0}; // next write position
176
177 // Previous prices for log-return computation.
178 Eigen::VectorXd prev_prices_;
179 bool has_prev_{false};
180
181 // Cached metric tensor.
182 mutable std::optional<Eigen::MatrixXd> current_metric_;
183 bool metric_dirty_{true};
184
185 /// @brief Recompute the correlation matrix and metric from the current window.
186 void recompute_metric();
187
188 /// @brief Compute the sample covariance from stored log-return observations.
189 [[nodiscard]] Eigen::MatrixXd compute_covariance() const;
190};
191
192// ---------------------------------------------------------------------------
193// MultiAssetLorentz
194// ---------------------------------------------------------------------------
195
196/// @brief Applies simultaneous Lorentz boosts to N correlated price series.
197///
198/// In the multi-asset setting, each asset has its own β_i = ΔP_i / (c · Δt).
199/// The off-diagonal metric terms (correlations) couple the boosts: a high-β
200/// move in asset A induces a "relativistic correction" in correlated asset B.
201///
202/// The transform is applied column-wise in the (N+1)-dimensional manifold:
203///
204/// γ_portfolio = 1 / sqrt(1 − β_portfolio²)
205///
206/// where β_portfolio = |Σ g_ij β_i β_j| (metric-weighted portfolio velocity).
208public:
209 /// @brief Result of a multi-asset Lorentz transform.
211 std::vector<double> adjusted_prices; ///< γ-scaled price adjustments.
212 double beta_portfolio; ///< Portfolio velocity ∈ [0, 1).
213 LorentzFactor gamma_portfolio; ///< Portfolio Lorentz factor.
214 std::vector<double> per_asset_beta; ///< Individual β_i values.
215 std::vector<double> per_asset_gamma; ///< Individual γ(β_i) values.
216 };
217
218 /// @brief Apply the multi-asset Lorentz transform.
219 ///
220 /// @param a Previous-bar event.
221 /// @param b Current-bar event.
222 /// @param metric (N+1)×(N+1) Lorentzian metric tensor.
223 /// @return TransformResult, or std::nullopt if inputs are invalid.
224 [[nodiscard]] static std::optional<TransformResult>
225 transform(const MultiAssetEvent& a,
226 const MultiAssetEvent& b,
227 const Eigen::MatrixXd& metric) noexcept;
228
229 /// @brief Compute portfolio β from individual asset velocities and the metric.
230 ///
231 /// β_portfolio = sqrt(g_ij β_i β_j) (metric-weighted speed).
232 ///
233 /// @param betas Per-asset β values.
234 /// @param metric (N+1)×(N+1) metric (only the spatial block is used).
235 /// @return Portfolio β, clamped to [0, BETA_MAX_SAFE].
236 [[nodiscard]] static double
237 portfolio_beta(const std::vector<double>& betas,
238 const Eigen::MatrixXd& metric) noexcept;
239};
240
241// ---------------------------------------------------------------------------
242// PortfolioGeodesic
243// ---------------------------------------------------------------------------
244
245/// @brief Geodesic in multi-asset spacetime: the optimal portfolio path.
246///
247/// In the (N+1)-dimensional financial spacetime manifold, the geodesic
248/// equation describes the "force-free" trajectory of a portfolio in the absence
249/// of external shocks. Deviations of actual price paths from the geodesic
250/// are interpreted as trading signals.
251///
252/// The geodesic is computed via Euler integration of the linearised geodesic
253/// equation for the flat (constant-metric) case:
254///
255/// d²x^μ / dτ² = 0 (flat manifold: Christoffel symbols vanish)
256///
257/// Under a constant metric, the geodesic is simply a straight line in
258/// spacetime coordinates — the "inertial" portfolio trajectory. Curvature
259/// effects arise from the time-varying metric (handled by passing updated
260/// metrics per step).
262public:
263 /// @brief A single step on the geodesic path.
265 MultiAssetEvent event; ///< Predicted portfolio event.
266 double proper_time; ///< Cumulative proper time τ.
267 double path_length; ///< Cumulative geodesic arc length.
268 double deviation; ///< |actual − predicted| in price space.
269 };
270
271 /// @brief Compute the geodesic from an initial event with given four-velocity.
272 ///
273 /// @param initial Starting multi-asset event.
274 /// @param four_velocity (N+1)-vector dx^μ/dτ at the initial point.
275 /// @param metric (N+1)×(N+1) metric tensor (assumed constant).
276 /// @param n_steps Number of integration steps.
277 /// @param dt Proper-time step size.
278 /// @return Sequence of GeodesicStep, or empty if inputs are invalid.
279 [[nodiscard]] static std::vector<GeodesicStep>
280 integrate(const MultiAssetEvent& initial,
281 const Eigen::VectorXd& four_velocity,
282 const Eigen::MatrixXd& metric,
283 std::size_t n_steps,
284 double dt) noexcept;
285
286 /// @brief Compute the geodesic deviation between the predicted and actual path.
287 ///
288 /// @param predicted Geodesic steps from integrate().
289 /// @param actual Sequence of observed MultiAssetEvents.
290 /// @return Per-step deviation ||predicted_prices − actual_prices||₂,
291 /// or empty if lengths mismatch.
292 [[nodiscard]] static std::vector<double>
293 deviation_series(const std::vector<GeodesicStep>& predicted,
294 const std::vector<MultiAssetEvent>& actual) noexcept;
295
296 /// @brief Compute portfolio weights along the geodesic path.
297 ///
298 /// At each step the geodesic four-velocity is normalised so that the
299 /// spatial components sum to the target gross exposure. Returns one
300 /// weight vector per step.
301 ///
302 /// @param steps Geodesic steps from integrate().
303 /// @param four_velocity Reference four-velocity (direction of travel).
304 /// @param gross_exposure Target portfolio gross exposure (sum of |weights|).
305 /// @return Per-step weight vectors.
306 [[nodiscard]] static std::vector<Eigen::VectorXd>
307 portfolio_weights(const std::vector<GeodesicStep>& steps,
308 const Eigen::VectorXd& four_velocity,
309 double gross_exposure = 1.0) noexcept;
310};
311
312} // namespace srfm::multi_asset
Builds the Lorentzian metric tensor from a rolling correlation matrix.
std::size_t n_assets() const noexcept
Return the number of assets.
std::optional< Eigen::MatrixXd > update(const std::vector< double > &prices)
Add a new bar observation and update the metric.
std::optional< Eigen::MatrixXd > current_metric() const noexcept
Return the current metric tensor (last computed).
std::optional< Eigen::MatrixXd > correlation_matrix() const noexcept
Return the rolling correlation matrix (spatial block only).
void reset() noexcept
Reset the window (clears all observations).
std::size_t observation_count() const noexcept
Return the number of observations seen so far.
const Config & config() const noexcept
Return the configuration.
N-dimensional spacetime interval between two MultiAssetEvents.
static std::optional< double > compute(const MultiAssetEvent &a, const MultiAssetEvent &b, const Eigen::MatrixXd &metric) noexcept
Compute ds² between two N-asset spacetime events.
static constexpr double kLightlikeEps
Threshold for LIGHTLIKE classification.
static IntervalType classify(double ds2) noexcept
Classify a pre-computed ds² value.
static std::optional< std::pair< double, IntervalType > > compute_and_classify(const MultiAssetEvent &a, const MultiAssetEvent &b, const Eigen::MatrixXd &metric) noexcept
Compute and classify in one call.
Applies simultaneous Lorentz boosts to N correlated price series.
static double portfolio_beta(const std::vector< double > &betas, const Eigen::MatrixXd &metric) noexcept
Compute portfolio β from individual asset velocities and the metric.
static std::optional< TransformResult > transform(const MultiAssetEvent &a, const MultiAssetEvent &b, const Eigen::MatrixXd &metric) noexcept
Apply the multi-asset Lorentz transform.
Geodesic in multi-asset spacetime: the optimal portfolio path.
static std::vector< double > deviation_series(const std::vector< GeodesicStep > &predicted, const std::vector< MultiAssetEvent > &actual) noexcept
Compute the geodesic deviation between the predicted and actual path.
static std::vector< Eigen::VectorXd > portfolio_weights(const std::vector< GeodesicStep > &steps, const Eigen::VectorXd &four_velocity, double gross_exposure=1.0) noexcept
Compute portfolio weights along the geodesic path.
static std::vector< GeodesicStep > integrate(const MultiAssetEvent &initial, const Eigen::VectorXd &four_velocity, const Eigen::MatrixXd &metric, std::size_t n_steps, double dt) noexcept
Compute the geodesic from an initial event with given four-velocity.
Physical and financial constants for the SRFM system.
Spacetime interval computations for N-asset events.
N-Asset Lorentzian Manifold for Special Relativistic Financial Mechanics.
IntervalType
Causal character of a spacetime interval.
Definition manifold.hpp:61
constexpr double LIGHTLIKE_THRESHOLD
|ds²| below this value is classified as LIGHTLIKE.
Lorentz factor γ = 1/√(1−β²). Always ≥ 1.0 for valid beta.
Definition types.hpp:33
std::size_t window_size
Rolling window (number of bars).
double regularisation_eps
Ridge regularisation for ill-conditioned Σ.
A snapshot of N correlated financial assets at a point in time.
int64_t timestamp
Unix epoch milliseconds.
std::vector< std::string > symbols
Asset ticker symbols (for labelling).
std::vector< double > prices
Mid-prices for each asset.
std::vector< double > volumes
Traded volumes for each asset.
bool is_valid() const noexcept
Validate that all vectors have equal length.
std::size_t n_assets() const noexcept
Return the number of assets represented.
Result of a multi-asset Lorentz transform.
std::vector< double > per_asset_gamma
Individual γ(β_i) values.
std::vector< double > per_asset_beta
Individual β_i values.
double beta_portfolio
Portfolio velocity ∈ [0, 1).
std::vector< double > adjusted_prices
γ-scaled price adjustments.
LorentzFactor gamma_portfolio
Portfolio Lorentz factor.
A single step on the geodesic path.
double proper_time
Cumulative proper time τ.
double path_length
Cumulative geodesic arc length.
MultiAssetEvent event
Predicted portfolio event.
double deviation
|actual − predicted| in price space.
Shared primitive types for the Special Relativity in Financial Modeling (SRFM) system.