98 lines
3.3 KiB
C++
98 lines
3.3 KiB
C++
#pragma once
|
|
#include <string>
|
|
#include <vector>
|
|
#include <unordered_map>
|
|
#include <queue>
|
|
#include <optional>
|
|
#include <fstream>
|
|
#include <sstream>
|
|
#include <stdexcept>
|
|
#include <algorithm>
|
|
|
|
#include "bar.hpp"
|
|
#include "events.hpp"
|
|
|
|
// ── DataHandler ───────────────────────────────────────────────────────────────
|
|
//
|
|
// Reads one CSV per symbol from data/<SYMBOL>.csv (columns: date,open,high,low,close,volume)
|
|
// and streams bars one at a time, publishing a MarketEvent per bar.
|
|
|
|
class DataHandler {
|
|
public:
|
|
std::vector<std::string> symbols;
|
|
bool continue_backtest = true;
|
|
std::size_t idx = 0;
|
|
|
|
DataHandler(const std::vector<std::string>& syms,
|
|
const std::string& data_dir,
|
|
std::queue<Event>& events)
|
|
: symbols(syms), events_(events)
|
|
{
|
|
for (const auto& sym : symbols) {
|
|
auto bars = load_csv(data_dir + "/" + sym + ".csv");
|
|
if (bars.empty())
|
|
throw std::runtime_error("No data for " + sym);
|
|
data_[sym] = std::move(bars);
|
|
}
|
|
// all symbols share the same date index (from first symbol)
|
|
dates_.reserve(data_[symbols[0]].size());
|
|
for (const auto& b : data_[symbols[0]])
|
|
dates_.push_back(b.date);
|
|
}
|
|
|
|
// Returns bar at idx-1 (latest streamed bar)
|
|
std::optional<Bar> get_latest_bar(const std::string& sym) const {
|
|
if (idx == 0) return std::nullopt;
|
|
return data_.at(sym)[idx - 1];
|
|
}
|
|
|
|
// Returns last n bars up to current idx
|
|
std::vector<Bar> get_latest_bars(const std::string& sym, std::size_t n) const {
|
|
std::size_t start = (idx > n) ? idx - n : 0;
|
|
const auto& v = data_.at(sym);
|
|
return { v.begin() + start, v.begin() + idx };
|
|
}
|
|
|
|
const std::string& current_date() const { return dates_[idx - 1]; }
|
|
const std::vector<std::string>& dates() const { return dates_; }
|
|
|
|
void update_bars() {
|
|
if (idx < dates_.size()) {
|
|
++idx;
|
|
events_.push(MarketEvent{});
|
|
} else {
|
|
continue_backtest = false;
|
|
}
|
|
}
|
|
|
|
private:
|
|
std::unordered_map<std::string, std::vector<Bar>> data_;
|
|
std::vector<std::string> dates_;
|
|
std::queue<Event>& events_;
|
|
|
|
static std::vector<Bar> load_csv(const std::string& path) {
|
|
std::ifstream f(path);
|
|
if (!f.is_open())
|
|
throw std::runtime_error("Cannot open: " + path);
|
|
|
|
std::vector<Bar> bars;
|
|
std::string line;
|
|
std::getline(f, line); // skip header
|
|
|
|
while (std::getline(f, line)) {
|
|
if (line.empty()) continue;
|
|
std::istringstream ss(line);
|
|
std::string tok;
|
|
Bar b;
|
|
std::getline(ss, b.date, ',');
|
|
std::getline(ss, tok, ','); b.open = std::stod(tok);
|
|
std::getline(ss, tok, ','); b.high = std::stod(tok);
|
|
std::getline(ss, tok, ','); b.low = std::stod(tok);
|
|
std::getline(ss, tok, ','); b.close = std::stod(tok);
|
|
std::getline(ss, tok, ','); b.volume = std::stol(tok);
|
|
bars.push_back(b);
|
|
}
|
|
return bars;
|
|
}
|
|
};
|