12#include <fmt/format.h>
23[[nodiscard]]
bool all_finite(std::span<const double> v)
noexcept {
25 if (!std::isfinite(x))
return false;
31constexpr std::size_t MIN_METRIC_SAMPLES = 2;
35[[nodiscard]]
bool is_degenerate_dispersion(
double sd,
double mu)
noexcept {
36 return sd <= 1e-12 * std::abs(mu);
43double PerformanceCalculator::mean(std::span<const double> v)
noexcept {
45 const double sum = std::accumulate(v.begin(), v.end(), 0.0);
46 return sum /
static_cast<double>(v.size());
49double PerformanceCalculator::stddev(std::span<const double> v,
50 double mean_val)
noexcept {
55 const double d = x - mean_val;
58 return std::sqrt(sq_sum /
static_cast<double>(v.size() - 1));
61double PerformanceCalculator::downside_stddev(std::span<const double> v,
62 double threshold)
noexcept {
67 if (v.empty())
return 0.0;
71 const double d = x - threshold;
75 return std::sqrt(sq_sum /
static_cast<double>(v.size()));
82 double risk_free_rate,
83 double annualisation)
noexcept {
84 if (returns.size() < MIN_METRIC_SAMPLES)
return std::nullopt;
85 if (!all_finite(returns))
return std::nullopt;
86 if (!std::isfinite(risk_free_rate))
return std::nullopt;
87 if (annualisation <= 0.0)
return std::nullopt;
89 const double mu = mean(returns);
90 const double sd = stddev(returns, mu);
94 if (is_degenerate_dispersion(sd, mu))
return std::nullopt;
97 return (mu - risk_free_rate) / sd * std::sqrt(annualisation);
104 double risk_free_rate,
105 double annualisation)
noexcept {
106 if (returns.size() < MIN_METRIC_SAMPLES)
return std::nullopt;
107 if (!all_finite(returns))
return std::nullopt;
108 if (!std::isfinite(risk_free_rate))
return std::nullopt;
109 if (annualisation <= 0.0)
return std::nullopt;
111 const double mu = mean(returns);
112 const double sd_dn = downside_stddev(returns, risk_free_rate);
115 if (sd_dn <= 0.0)
return std::nullopt;
118 return (mu - risk_free_rate) / sd_dn * std::sqrt(annualisation);
125 if (returns.empty())
return std::nullopt;
126 if (!all_finite(returns))
return std::nullopt;
133 for (
double r : returns) {
138 const double dd = (peak - equity) / peak;
139 if (dd > max_dd) max_dd = dd;
149 std::span<const double> strategy_returns,
150 std::span<const double> benchmark_returns,
151 std::span<const double> gamma_factors)
noexcept {
153 const std::size_t n = strategy_returns.size();
154 if (n < MIN_METRIC_SAMPLES)
return std::nullopt;
155 if (benchmark_returns.size() != n)
return std::nullopt;
156 if (gamma_factors.size() != n)
return std::nullopt;
157 if (!all_finite(strategy_returns))
return std::nullopt;
158 if (!all_finite(benchmark_returns))
return std::nullopt;
159 if (!all_finite(gamma_factors))
return std::nullopt;
162 std::vector<double> active(n);
163 for (std::size_t i = 0; i < n; ++i) {
164 active[i] = strategy_returns[i] - benchmark_returns[i];
167 const double mu_active = mean(active);
168 const double sd_active = stddev(active, mu_active);
169 if (is_degenerate_dispersion(sd_active, mu_active))
return std::nullopt;
172 const double mu_gamma = mean(gamma_factors);
175 return (mu_active * mu_gamma) / sd_active;
181 : effective_mass_(effective_mass) {}
186 if (!std::isfinite(beta.value))
return std::nullopt;
189 const double beta2 = beta.value * beta.value;
190 const double denom = std::sqrt(1.0 - beta2);
191 if (denom <= 0.0)
return std::nullopt;
196std::optional<LorentzCorrectedSeries>
198 if (bars.empty())
return std::nullopt;
199 if (effective_mass_ <= 0.0)
return std::nullopt;
201 const std::size_t n = bars.size();
206 for (
const auto& bar : bars) {
207 const auto g_opt = lorentz_gamma(bar.beta);
209 const double g = g_opt.value_or(1.0);
222 "Sharpe={:.4f} Sortino={:.4f} MaxDrawdown={:.4f} GammaIR={:.4f}",
243 "┌─────────────────────────────────────────────────────────┐\n"
244 "│ Relativistic Backtester — Side-by-Side │\n"
245 "├──────────────────┬──────────────┬──────────────┬────────┤\n"
246 "│ Metric │ Raw │ Relativistic │ Lift │\n"
247 "├──────────────────┼──────────────┼──────────────┼────────┤\n"
248 "│ Sharpe Ratio │ {:9.4f} │ {:9.4f} │ {:+.4f}│\n"
249 "│ Sortino Ratio │ {:9.4f} │ {:9.4f} │ {:+.4f}│\n"
250 "│ Max Drawdown │ {:9.4f} │ {:9.4f} │ {:+.4f}│\n"
251 "│ γ-Weighted IR │ {:9.4f} │ {:9.4f} │ {:+.4f}│\n"
252 "├──────────────────┴──────────────┴──────────────┴────────┤\n"
253 "│ Mean γ: {:.4f} Max γ applied: {:.4f} IR lift: {:.4f}x │\n"
254 "└──────────────────────────────────────────────────────────┘\n",
Relativistic Backtester — AGT-05 public API.
static std::optional< double > lorentz_gamma(BetaVelocity beta) noexcept
LorentzSignalAdjuster(double effective_mass=1.0)
std::optional< LorentzCorrectedSeries > adjust(std::span< const BarData > bars) const noexcept
Physical and financial constants for the SRFM system.
static constexpr double BETA_MAX_SAFE
std::string to_string() const
Formatted comparison table.
double max_gamma_applied
Maximum γ multiplier actually applied (capped at BacktestConfig::max_gamma).
double drawdown_delta() const noexcept
raw.mdd − rel.mdd (positive = improvement)
double sharpe_lift() const noexcept
rel.sharpe − raw.sharpe
double ir_lift() const noexcept
rel.ir − raw.ir
double sortino_lift() const noexcept
rel.sortino − raw.sortino
PerformanceMetrics relativistic
Metrics from γ-scaled position signals.
PerformanceMetrics raw
Metrics from unmodified (unit-position) signals.
double mean_gamma
Mean Lorentz factor γ across all bars.
A complete set of relativistic corrections for one return series.
std::vector< double > adjusted_signals
γ_t × raw_signal_t
std::vector< double > gamma_factors
γ(β_t) for every bar