60static std::vector<std::string>
split_csv(
const std::string& line) {
61 std::vector<std::string> fields;
63 std::istringstream ss(line);
64 while (std::getline(ss, field,
',')) {
66 auto start = field.find_first_not_of(
" \t\r\n");
67 auto end = field.find_last_not_of(
" \t\r\n");
68 if (start != std::string::npos) {
69 fields.push_back(field.substr(start, end - start + 1));
79 if (s.empty())
return std::nullopt;
82 double val = std::stod(s, &pos);
83 if (pos != s.size())
return std::nullopt;
84 if (!std::isfinite(val))
return std::nullopt;
92static int find_col(
const std::vector<std::string>& header,
93 const std::string& name) {
94 for (
int i = 0; i < static_cast<int>(header.size()); ++i) {
95 std::string h = header[
static_cast<std::size_t
>(i)];
96 std::transform(h.begin(), h.end(), h.begin(), ::tolower);
97 if (h == name)
return i;
104static std::optional<std::vector<OhlcvBar>>
load_csv(
const std::string& path,
106 std::ifstream file(path);
107 if (!file.is_open()) {
108 err =
"Cannot open input file: " + path;
112 std::vector<OhlcvBar> bars;
116 if (!std::getline(file, line)) {
117 err =
"Empty CSV file: " + path;
122 int col_ts =
find_col(header,
"timestamp");
123 int col_open =
find_col(header,
"open");
124 int col_high =
find_col(header,
"high");
125 int col_low =
find_col(header,
"low");
126 int col_close =
find_col(header,
"close");
127 int col_volume =
find_col(header,
"volume");
129 if (col_close < 0 || col_volume < 0) {
130 err =
"CSV missing required columns 'close' or 'volume'";
134 std::size_t row_idx = 0;
135 std::size_t skipped = 0;
136 while (std::getline(file, line)) {
138 if (line.empty())
continue;
141 if (
static_cast<int>(fields.size()) <= std::max(col_close, col_volume)) {
146 auto maybe_close =
safe_parse_double(fields[
static_cast<std::size_t
>(col_close)]);
147 auto maybe_volume =
safe_parse_double(fields[
static_cast<std::size_t
>(col_volume)]);
149 if (!maybe_close || !maybe_volume) {
153 if (*maybe_close <= 0.0 || *maybe_volume < 0.0) {
159 bar.
timestamp = (col_ts >= 0) ? fields[
static_cast<std::size_t
>(col_ts)] : std::to_string(row_idx);
160 bar.
close = *maybe_close;
161 bar.
volume = *maybe_volume;
180 std::cerr <<
"[regime_validator] Skipped " << skipped <<
" non-finite rows\n";
197 const std::vector<OhlcvBar>& bars,
198 const std::string& ticker)
200 const std::size_t n = bars.size();
202 std::cerr <<
"[" << ticker <<
"] Too few bars (" << n <<
") — need at least 3\n";
208 std::vector<srfm::manifold::SpacetimeEvent> raw_events;
209 raw_events.reserve(n);
210 for (std::size_t i = 0; i < n; ++i) {
211 double momentum = 0.0;
212 if (i > 0 && bars[i - 1].close > 0.0) {
213 momentum = (bars[i].close - bars[i - 1].close) / bars[i - 1].close;
215 raw_events.push_back({
216 .time =
static_cast<double>(i),
217 .price = bars[i].close,
218 .volume = bars[i].volume,
219 .momentum = momentum,
227 auto maybe_prev0 = normalizer.
normalize(raw_events[0]);
230 (void)normalizer.
normalize(raw_events[1]);
234 std::vector<srfm::manifold::SpacetimeEvent> all_normalized;
235 all_normalized.reserve(n);
236 for (
const auto& ev : raw_events) {
238 if (maybe_norm.has_value()) {
239 all_normalized.push_back(*maybe_norm);
248 auto geo_signals = geo_calc.
compute(all_normalized);
252 std::vector<ClassifiedBar> results;
253 results.reserve(n - 2);
258 auto maybe_p = norm3.
normalize(raw_events[0]);
262 for (std::size_t i = 1; i + 1 < n; ++i) {
265 norm3, prev_normalized, raw_events[i]);
267 auto maybe_p = norm3.
normalize(raw_events[i]);
271 if (!maybe_type.has_value()) {
277 all_normalized[i - 1], all_normalized[i]);
278 double beta = maybe_beta.value_or(0.0);
281 double next_abs_ret = 0.0;
282 double next_ret = 0.0;
283 if (bars[i].close > 0.0) {
284 next_ret = bars[i + 1].close / bars[i].close - 1.0;
285 next_abs_ret = std::abs(next_ret);
289 double geo_dev = 0.0;
290 if (i < geo_signals.size() && geo_signals[i].is_valid) {
291 geo_dev = geo_signals[i].geodesic_deviation;
298 .interval_type = std::string(type_str),
299 .next_bar_abs_return = next_abs_ret,
300 .next_bar_return = next_ret,
302 .geodesic_deviation = geo_dev,
312 const std::string& ticker,
313 const std::vector<ClassifiedBar>& bars,
314 const std::string& output_path,
317 std::ofstream out(output_path);
318 if (!out.is_open()) {
319 err =
"Cannot open output file: " + output_path;
323 out <<
"ticker,bar_index,interval_type,next_bar_abs_return,next_bar_return,beta,geodesic_deviation\n";
325 for (
const auto& b : bars) {
327 << b.bar_index <<
","
328 << b.interval_type <<
","
331 out << b.next_bar_abs_return <<
","
332 << b.next_bar_return <<
","
334 << b.geodesic_deviation <<
"\n";
347static std::optional<Args>
parse_args(
int argc,
char* argv[]) {
349 for (
int i = 1; i < argc - 1; ++i) {
350 std::string key(argv[i]);
351 std::string val(argv[i + 1]);
352 if (key ==
"--input") {
355 }
else if (key ==
"--output") {
358 }
else if (key ==
"--ticker") {
366 if (args.
ticker.empty()) {
374int main(
int argc,
char* argv[]) {
376 if (!maybe_args.has_value()) {
377 std::cerr <<
"Usage: regime_validator --input <csv> --output <csv> "
378 "[--ticker <name>]\n";
382 const Args& args = *maybe_args;
385 std::string load_err;
387 if (!maybe_bars.has_value()) {
388 std::cerr <<
"[FATAL] " << load_err <<
"\n";
391 std::vector<OhlcvBar> bars = std::move(*maybe_bars);
394 std::cerr <<
"[FATAL] No valid bars loaded from " << args.
input_path <<
"\n";
398 std::cout <<
"[" << args.
ticker <<
"] Loaded " << bars.size() <<
" bars\n";
403 if (classified.empty()) {
404 std::cerr <<
"[FATAL] No bars classified — check data quality\n";
409 std::size_t n_tl = 0, n_sl = 0, n_ll = 0;
410 for (
const auto& b : classified) {
411 if (b.interval_type ==
"Timelike") ++n_tl;
412 else if (b.interval_type ==
"Spacelike") ++n_sl;
416 std::cout <<
"[" << args.
ticker <<
"] Classified " << classified.size() <<
" bars\n";
417 std::cout <<
" TIMELIKE: " << n_tl <<
" (" << (100.0 * n_tl / classified.size()) <<
"%)\n";
418 std::cout <<
" SPACELIKE: " << n_sl <<
" (" << (100.0 * n_sl / classified.size()) <<
"%)\n";
419 std::cout <<
" LIGHTLIKE: " << n_ll <<
" (" << (100.0 * n_ll / classified.size()) <<
"%)\n";
422 std::string write_err;
424 std::cerr <<
"[FATAL] " << write_err <<
"\n";
428 std::cout <<
"[" << args.
ticker <<
"] Output written to " << args.
output_path <<
"\n";
std::optional< manifold::SpacetimeEvent > normalize(const manifold::SpacetimeEvent &raw) noexcept
static std::optional< double > beta(const SpacetimeEvent &a, const SpacetimeEvent &b, double c_market=constants::SPEED_OF_INFORMATION) noexcept
static std::optional< IntervalType > process(srfm::CoordinateNormalizer &normalizer, const SpacetimeEvent &prev_normalized, const SpacetimeEvent &curr_raw) noexcept
std::vector< GeodesicSignal > compute(const std::vector< manifold::SpacetimeEvent > &events) const noexcept
static MetricTensor make_minkowski(double time_scale=1.0, double spatial_scale=1.0)
Physical and financial constants for the SRFM system.
Geodesic Deviation Signal — AGT-07 public API.
Spacetime Market Manifold — AGT-02 public API (implemented by AGT-06).
const char * to_string(IntervalType t) noexcept
Convert IntervalType to a human-readable string.
CoordinateNormalizer — rolling z-score normalizer for SpacetimeEvent.
int main(int argc, char *argv[])
static std::optional< double > safe_parse_double(const std::string &s)
Safely parse a double from a string. Returns nullopt on failure.
static std::optional< std::vector< OhlcvBar > > load_csv(const std::string &path, std::string &err)
static std::vector< ClassifiedBar > classify_bars(const std::vector< OhlcvBar > &bars, const std::string &ticker)
static std::vector< std::string > split_csv(const std::string &line)
Split a CSV line on commas (no quote handling needed for numeric data).
static bool write_output(const std::string &ticker, const std::vector< ClassifiedBar > &bars, const std::string &output_path, std::string &err)
static std::optional< Args > parse_args(int argc, char *argv[])
static int find_col(const std::vector< std::string > &header, const std::string &name)
Find the column index of a header name (case-insensitive).
double next_bar_return
Signed next-bar return (for backtesting)
double geodesic_deviation
std::string interval_type
double next_bar_abs_return
A point in 4D spacetime (t, x, y, z).
Tensor Calculus & Covariance Engine — AGT-04 public API.