Special Relativity in Financial Modeling 1.0.0
Lorentz transforms, spacetime classification, and geodesic price paths for quantitative finance
Loading...
Searching...
No Matches
relativistic_optimizer.cpp
Go to the documentation of this file.
1/// @file src/relativistic_optimizer.cpp
2/// @brief Implementation of RelativisticPortfolio optimizer.
3///
4/// See include/relativistic_optimizer.hpp for the public API contract.
5///
6/// ## Algorithm
7/// The optimization problem is:
8/// minimise risk_tolerance · w^T Σ_st w
9/// subject to w^T μ_rel ≥ r_target
10/// Σ_i w_i = 1, w_i ≥ 0
11///
12/// Solved via projected gradient descent:
13/// w_{k+1} = Π_simplex(w_k − α · ∇_w L(w_k))
14/// where
15/// L(w) = risk_tolerance · w^T Σ_st w − λ · (w^T μ_rel − r_target)
16/// ∇_w L(w) = 2 · risk_tolerance · Σ_st · w − λ · μ_rel
17///
18/// λ is adaptively set so the return constraint is enforced at each step.
19
21
22#include "srfm/constants.hpp"
23
24#include <Eigen/Dense>
25#include <algorithm>
26#include <cmath>
27#include <numeric>
28
29namespace srfm::portfolio {
30
31// ─── Construction ─────────────────────────────────────────────────────────────
32
34 : config_(config) {}
35
36// ─── Asset management ─────────────────────────────────────────────────────────
37
39 double expected_return) {
40 events_.push_back(std::move(event));
41 expected_returns_.push_back(expected_return);
42}
43
44std::size_t RelativisticPortfolio::n_assets() const noexcept {
45 return events_.size();
46}
47
49 events_.clear();
50 expected_returns_.clear();
51}
52
53// ─── Lorentz factor ───────────────────────────────────────────────────────────
54
55double RelativisticPortfolio::compute_beta(const AssetEvent& e) const noexcept {
56 // Approximate β from the event's price coordinate relative to origin.
57 // β = |P| / (c_market · |t|) when |t| > 0, else 0.
58 const double c = config_.c_market;
59 if (std::abs(e.t) < constants::FLOAT_EPSILON) {
60 return 0.0;
61 }
62 const double beta = std::abs(e.P) / (c * std::abs(e.t));
63 // Clamp to BETA_MAX_SAFE to prevent γ divergence.
64 return std::min(beta, constants::BETA_MAX_SAFE);
65}
66
67double RelativisticPortfolio::gamma_factor(double beta) noexcept {
68 // γ(β) = 1 / √(1 − β²), clamped for stability.
69 const double beta_clamped = std::min(std::abs(beta), constants::BETA_MAX_SAFE);
70 const double denom = std::sqrt(1.0 - beta_clamped * beta_clamped);
71 // denom should be > 0 by BETA_MAX_SAFE clamping, but guard anyway.
72 if (denom < constants::FLOAT_EPSILON) {
73 return 1.0 / std::sqrt(1.0 - constants::BETA_MAX_SAFE *
75 }
76 return 1.0 / denom;
77}
78
79// ─── Relativistic returns ─────────────────────────────────────────────────────
80
81std::optional<Eigen::VectorXd>
83 const std::size_t N = events_.size();
84 if (N == 0) {
85 return std::nullopt;
86 }
87
88 Eigen::VectorXd mu_rel(static_cast<int>(N));
89 for (std::size_t i = 0; i < N; ++i) {
90 const double beta = compute_beta(events_[i]);
91 const double gamma = gamma_factor(beta);
92 mu_rel(static_cast<int>(i)) = gamma * expected_returns_[i];
93 }
94 return mu_rel;
95}
96
97// ─── Spacetime covariance ─────────────────────────────────────────────────────
98
99std::optional<Eigen::MatrixXd>
101 if (events_.size() < 2) {
102 return std::nullopt;
103 }
104 MinkowskiCovariance mc(config_.c_market);
105 for (const auto& ev : events_) {
106 mc.add_asset(ev);
107 }
109}
110
111// ─── Simplex projection ───────────────────────────────────────────────────────
112
113Eigen::VectorXd
114RelativisticPortfolio::project_simplex(const Eigen::VectorXd& v) noexcept {
115 // Algorithm: Duchi et al. (2008), "Efficient Projections onto the l1-Ball".
116 // Projects onto Δ_N = { w ∈ R^N : Σw_i = 1, w_i >= 0 }.
117 const int n = static_cast<int>(v.size());
118
119 // Sort descending.
120 Eigen::VectorXd u = v;
121 std::sort(u.data(), u.data() + n, std::greater<double>());
122
123 // Compute rho: largest index where u_j - (cumsum_j - 1)/j > 0.
124 double cumsum = 0.0;
125 int rho = 0;
126 for (int j = 0; j < n; ++j) {
127 cumsum += u(j);
128 if (u(j) - (cumsum - 1.0) / static_cast<double>(j + 1) > 0.0) {
129 rho = j;
130 }
131 }
132
133 // Compute threshold theta.
134 double cumsum_rho = 0.0;
135 for (int j = 0; j <= rho; ++j) {
136 cumsum_rho += u(j);
137 }
138 const double theta = (cumsum_rho - 1.0) / static_cast<double>(rho + 1);
139
140 // Project.
141 Eigen::VectorXd w(n);
142 for (int i = 0; i < n; ++i) {
143 w(i) = std::max(v(i) - theta, 0.0);
144 }
145 return w;
146}
147
148// ─── Geodesic gradient ────────────────────────────────────────────────────────
149
150Eigen::VectorXd
151RelativisticPortfolio::geodesic_gradient(const Eigen::MatrixXd& sigma_st,
152 const Eigen::VectorXd& w) noexcept {
153 // Gradient of d²_geo(w) = w^T Σ_st w is 2 Σ_st w.
154 return 2.0 * sigma_st * w;
155}
156
157// ─── optimize_weights ─────────────────────────────────────────────────────────
158
159std::optional<OptimizationResult>
161 double risk_tolerance) const noexcept {
162 const std::size_t N = events_.size();
163 if (N < 2) {
164 return std::nullopt;
165 }
166
167 // Build spacetime covariance matrix (risk metric).
168 auto sigma_opt = spacetime_covariance();
169 if (!sigma_opt) {
170 return std::nullopt;
171 }
172 Eigen::MatrixXd sigma_st = *sigma_opt;
173
174 // Apply Tikhonov regularisation: Σ_reg = Σ_st + λI.
175 // This prevents singular or ill-conditioned geodesic metrics.
176 sigma_st += config_.regularisation *
177 Eigen::MatrixXd::Identity(static_cast<int>(N),
178 static_cast<int>(N));
179
180 // Relativistic returns μ_rel.
181 auto mu_opt = relativistic_returns();
182 if (!mu_opt) {
183 return std::nullopt;
184 }
185 const Eigen::VectorXd mu_rel = *mu_opt;
186
187 // Initialise weights to uniform (maximum entropy starting point).
188 Eigen::VectorXd w = Eigen::VectorXd::Constant(
189 static_cast<int>(N), 1.0 / static_cast<double>(N));
190
191 const double alpha = config_.step_size;
192 const double tol = config_.convergence_tol;
193 const int max_iter = config_.max_iterations;
194
195 int iter = 0;
196 bool converged = false;
197
198 for (iter = 0; iter < max_iter; ++iter) {
199 // Geodesic risk gradient: ∇_w (risk_tolerance · w^T Σ_st w)
200 Eigen::VectorXd grad = risk_tolerance * geodesic_gradient(sigma_st, w);
201
202 // Return constraint penalty gradient.
203 // L_aug = risk * w^T Σ w − penalty * max(0, w^T μ − r_target)
204 // Penalty: when return falls short, pull weights toward high-μ assets.
205 const double current_return = w.dot(mu_rel);
206 if (current_return < target_return) {
207 // Gradient ascent on return: subtract mu_rel to reduce the deficit.
208 const double penalty = risk_tolerance;
209 grad -= penalty * mu_rel;
210 }
211
212 // Projected gradient step.
213 Eigen::VectorXd w_new = project_simplex(w - alpha * grad);
214
215 // Check convergence.
216 const double step_norm = (w_new - w).norm();
217 w = w_new;
218
219 if (step_norm < tol) {
220 converged = true;
221 ++iter;
222 break;
223 }
224 }
225
226 // Compute final risk and return.
227 const double geo_risk = risk_tolerance * w.dot(sigma_st * w);
228 const double exp_ret = w.dot(mu_rel);
229
230 return OptimizationResult{
231 .weights = w,
232 .geodesic_risk = geo_risk,
233 .expected_return = exp_ret,
234 .iterations = iter,
235 .converged = converged,
236 };
237}
238
239} // namespace srfm::portfolio
std::optional< Eigen::MatrixXd > compute_spacetime_covariance() const noexcept
std::size_t n_assets() const noexcept
Return the number of assets currently in the portfolio.
std::optional< Eigen::MatrixXd > spacetime_covariance() const noexcept
std::optional< Eigen::VectorXd > relativistic_returns() const noexcept
RelativisticPortfolio(OptimizerConfig config=OptimizerConfig{}) noexcept
Construct with optional configuration.
void add_asset(AssetEvent event, double expected_return)
void clear() noexcept
Remove all assets from the portfolio.
std::optional< OptimizationResult > optimize_weights(double target_return, double risk_tolerance=1.0) const noexcept
Physical and financial constants for the SRFM system.
static constexpr double FLOAT_EPSILON
General floating-point comparison epsilon.
Definition constants.hpp:25
static constexpr double BETA_MAX_SAFE
Definition constants.hpp:17
Relativistic Portfolio Optimization on the Financial Manifold.
Result of a single portfolio optimization run.
Eigen::VectorXd weights
Optimal asset weights (sum to 1, >= 0)
Tuning parameters for the relativistic portfolio optimizer.
double c_market
Speed-of-information parameter for Lorentz factor computation.