#include "raid/data/io.hpp" #include "raid/core/error.hpp" #include #include #include namespace raid::data { std::map read_bars_csv(const std::string& path, Interval iv) { std::ifstream f(path); RAID_REQUIRE(f.good(), io, "cannot open " + path); std::string line; std::getline(f, line); RAID_REQUIRE(line.rfind("symbol,timestamp_utc,open,high,low,close,volume", 0) == 0, io, "unexpected CSV header in " + path + ": " + line); std::map out; usize ln = 1; while (std::getline(f, line)) { ++ln; if (line.empty()) continue; std::string cells[7]; usize c = 0, start = 0; for (usize i = 0; i <= line.size() && c < 7; ++i) if (i == line.size() || line[i] == ',') { cells[c++] = line.substr(start, i - start); start = i + 1; } RAID_REQUIRE(c == 7, io, "bad CSV row " + std::to_string(ln) + " in " + path); auto num = [&](const std::string& s) { f64 v; const auto r = std::from_chars(s.data(), s.data() + s.size(), v); RAID_REQUIRE(r.ec == std::errc{}, io, "bad number '" + s + "' row " + std::to_string(ln)); return v; }; auto& bs = out[cells[0]]; if (bs.symbol.empty()) { bs.symbol = cells[0]; bs.instrument_id = "synthetic:" + cells[0]; bs.interval = iv; bs.adjustment = "synthetic"; } bs.push_back(timestamp_from_iso(cells[1]), num(cells[2]), num(cells[3]), num(cells[4]), num(cells[5]), num(cells[6])); } for (auto& [k, bs] : out) bs.index_sessions(); return out; } void write_bars_csv(const std::string& path, const BarSeries& b) { std::ofstream f(path); RAID_REQUIRE(f.good(), io, "cannot write " + path); f << "symbol,timestamp_utc,open,high,low,close,volume\n"; f.precision(10); for (usize i = 0; i < b.size(); ++i) f << b.symbol << ',' << timestamp_iso(b.t_open[i]) << ',' << b.open[i] << ',' << b.high[i] << ',' << b.low[i] << ',' << b.close[i] << ',' << b.volume[i] << '\n'; } } // namespace raid::data