13#include "../../include/srfm/engine/n_asset_engine.hpp"
25 : universe_(std::move(universe))
31std::optional<std::monostate>
34 if (
static_cast<int>(bars.size()) != universe_.n()) {
39 history_.push_back(std::vector<OHLCVBar>(bars.begin(), bars.end()));
42 const int max_history = cfg_.lookback_bars * 2;
43 while (
static_cast<int>(history_.size()) > max_history) {
47 return std::monostate{};
52std::optional<Eigen::MatrixXd>
53NAssetEngine::estimate_covariance() const noexcept {
54 const int N = universe_.
n();
55 const int T =
static_cast<int>(history_.size());
63 if (T < 2) {
return std::nullopt; }
65 const int n_returns = T - 1;
68 Eigen::MatrixXd returns(n_returns, N);
70 for (
int t = 0; t < n_returns; ++t) {
71 const auto& prev = history_[
static_cast<std::size_t
>(t)];
72 const auto& curr = history_[
static_cast<std::size_t
>(t + 1)];
74 for (
int i = 0; i < N; ++i) {
75 double prev_close = prev[
static_cast<std::size_t
>(i)].close;
76 double curr_close = curr[
static_cast<std::size_t
>(i)].close;
79 if (prev_close <= 0.0 || curr_close <= 0.0) {
83 returns(t, i) = std::log(curr_close / prev_close);
88 Eigen::VectorXd mean = returns.colwise().mean();
89 Eigen::MatrixXd demeaned = returns.rowwise() - mean.transpose();
96 Eigen::MatrixXd cov = demeaned.transpose() * demeaned;
98 cov += 1e-8 * Eigen::MatrixXd::Identity(N, N);
102 Eigen::MatrixXd cov = (demeaned.transpose() * demeaned) /
103 static_cast<double>(n_returns - 1);
107 cov += 1e-8 * Eigen::MatrixXd::Identity(N, N);
115NAssetEngine::compute_beta(
double prev_close,
double curr_close)
const noexcept {
116 if (prev_close <= 0.0) {
return std::nullopt; }
118 double delta = curr_close - prev_close;
119 double raw_beta = std::abs(delta / (cfg_.c_market * prev_close));
123 if (raw_beta >= beta_max) {
132std::optional<std::pair<double, IntervalType>>
133NAssetEngine::compute_portfolio_interval(
134 const NAssetManifold& manifold)
const noexcept {
135 const int T =
static_cast<int>(history_.size());
136 if (T < 2) {
return std::nullopt; }
138 const int N = universe_.n();
141 const auto& prev_bars = history_[
static_cast<std::size_t
>(T - 2)];
142 const auto& curr_bars = history_[
static_cast<std::size_t
>(T - 1)];
144 Eigen::VectorXd prev_prices(N), curr_prices(N);
145 for (
int i = 0; i < N; ++i) {
146 prev_prices(i) = prev_bars[
static_cast<std::size_t
>(i)].close;
147 curr_prices(i) = curr_bars[
static_cast<std::size_t
>(i)].close;
150 double t_prev = prev_bars[0].timestamp;
151 double t_curr = curr_bars[0].timestamp;
155 if (!ev_a || !ev_b) {
return std::nullopt; }
157 NAssetInterval interval_calc;
158 auto result = interval_calc.compute(*ev_a, *ev_b, manifold);
159 if (!result) {
return std::nullopt; }
161 return std::make_pair(result->ds_sq, result->type);
167 if (!
ready()) {
return std::nullopt; }
169 const int N = universe_.
n();
172 auto cov_opt = estimate_covariance();
173 if (!cov_opt) {
return std::nullopt; }
177 if (!manifold_opt) {
return std::nullopt; }
181 const int T =
static_cast<int>(history_.size());
182 const auto& prev_bars = history_[
static_cast<std::size_t
>(T - 2)];
183 const auto& curr_bars = history_[
static_cast<std::size_t
>(T - 1)];
187 output.
timestamp = curr_bars[0].timestamp;
188 output.assets.reserve(
static_cast<std::size_t
>(N));
190 for (
int i = 0; i < N; ++i) {
191 const OHLCVBar& prev = prev_bars[
static_cast<std::size_t
>(i)];
192 const OHLCVBar& curr = curr_bars[
static_cast<std::size_t
>(i)];
195 auto beta_opt = compute_beta(prev.
close, curr.close);
196 if (!beta_opt) {
return std::nullopt; }
197 double beta = *beta_opt;
200 double gamma = 1.0 / std::sqrt(1.0 - beta * beta);
206 double p_rel = gamma * m_eff * curr.close;
210 Eigen::VectorXd prev_p(1), curr_p(1);
211 prev_p(0) = prev.
close;
212 curr_p(0) = curr.close;
218 Eigen::MatrixXd cov_1x1(1, 1);
219 cov_1x1(0, 0) = (*cov_opt)(i, i);
221 IntervalType asset_regime = IntervalType::SPACELIKE;
223 if (sub_manifold_opt) {
228 auto ir = calc.
compute(*ea, *eb, *sub_manifold_opt);
230 asset_regime = ir->type;
241 res.
regime = asset_regime;
243 output.assets.push_back(std::move(res));
247 auto portfolio_opt = compute_portfolio_interval(manifold);
248 if (!portfolio_opt) {
249 output.portfolio_interval_sq = 0.0;
250 output.portfolio_regime = IntervalType::LIGHTLIKE;
252 output.portfolio_interval_sq = portfolio_opt->first;
253 output.portfolio_regime = portfolio_opt->second;
259std::optional<EngineOutput>
261 auto r = ingest(bars);
262 if (!r) {
return std::nullopt; }
std::optional< std::monostate > ingest(std::span< const OHLCVBar > bars) noexcept
Ingest one bar for every asset in the universe.
std::optional< EngineOutput > ingest_and_process(std::span< const OHLCVBar > bars) noexcept
Ingest bars then immediately process.
bool ready() const noexcept
Return true if enough bars have been accumulated.
std::optional< EngineOutput > process() const noexcept
Run the full pipeline on accumulated history.
NAssetEngine(AssetUniverse universe, EngineConfig cfg={}) noexcept
Construct an engine for the given asset universe.
Computes spacetime intervals between N-asset events.
std::optional< IntervalResult > compute(const NAssetEvent &a, const NAssetEvent &b, const NAssetManifold &manifold) const noexcept
Compute ds² = g_μν Δx^μ Δx^ν between two events.
(N+1)-dimensional Lorentzian manifold for N financial assets.
static std::optional< NAssetManifold > make(int n, Eigen::MatrixXd cov, double c_market=1.0) noexcept
Factory: validate inputs then construct.
constexpr double ENGINE_BETA_MAX_SAFE
Maximum safe beta value (mirrors BETA_MAX_SAFE from momentum.hpp).
Relativistic momentum result for a single asset.
double relativistic_momentum
p_rel = γ × m_eff × close.
double m_eff
Effective mass (volume / ADV_baseline).
std::string asset_name
Name of the asset.
double gamma
Lorentz gamma factor.
IntervalType regime
Interval type for this asset.
double beta
Lorentz beta (normalised velocity).
The universe of assets processed by the engine.
int n() const noexcept
Return the number of assets.
std::vector< std::string > names
Asset names in order.
Engine configuration parameters.
int lookback_bars
Bars required before covariance is valid.
double adv_baseline
ADV baseline for effective mass.
double c_market
Market speed of light.
Aggregated engine output for one process() call.
double timestamp
Timestamp of latest bar.
A single OHLCV bar for one asset.
double close
Closing price.
double timestamp
Bar timestamp (seconds since epoch or bar index).
static std::optional< NAssetEvent > make(double t, Eigen::VectorXd prices) noexcept
Factory: validate and construct an NAssetEvent.