Агрегация
This commit is contained in:
@@ -1,87 +1,48 @@
|
||||
#include "aggregation.hpp"
|
||||
#include <map>
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <cmath>
|
||||
|
||||
std::vector<DayStats> aggregate_days(const std::vector<Record>& records) {
|
||||
// Группируем записи по дням
|
||||
std::map<DayIndex, std::vector<const Record*>> day_records;
|
||||
// Накопители для каждого дня
|
||||
struct DayAccumulator {
|
||||
double avg_sum = 0.0;
|
||||
double open_min = std::numeric_limits<double>::max();
|
||||
double open_max = std::numeric_limits<double>::lowest();
|
||||
double close_min = std::numeric_limits<double>::max();
|
||||
double close_max = std::numeric_limits<double>::lowest();
|
||||
int64_t count = 0;
|
||||
};
|
||||
|
||||
std::map<DayIndex, DayAccumulator> days;
|
||||
|
||||
for (const auto& r : records) {
|
||||
DayIndex day = static_cast<DayIndex>(r.timestamp) / 86400;
|
||||
day_records[day].push_back(&r);
|
||||
auto& acc = days[day];
|
||||
|
||||
double avg = (r.low + r.high) / 2.0;
|
||||
acc.avg_sum += avg;
|
||||
acc.open_min = std::min(acc.open_min, r.open);
|
||||
acc.open_max = std::max(acc.open_max, r.open);
|
||||
acc.close_min = std::min(acc.close_min, r.close);
|
||||
acc.close_max = std::max(acc.close_max, r.close);
|
||||
acc.count++;
|
||||
}
|
||||
|
||||
std::vector<DayStats> result;
|
||||
result.reserve(day_records.size());
|
||||
result.reserve(days.size());
|
||||
|
||||
for (auto& [day, recs] : day_records) {
|
||||
// Сортируем по timestamp для определения first/last
|
||||
std::sort(recs.begin(), recs.end(),
|
||||
[](const Record* a, const Record* b) {
|
||||
return a->timestamp < b->timestamp;
|
||||
});
|
||||
|
||||
for (const auto& [day, acc] : days) {
|
||||
DayStats stats;
|
||||
stats.day = day;
|
||||
stats.low = std::numeric_limits<double>::max();
|
||||
stats.high = std::numeric_limits<double>::lowest();
|
||||
stats.open = recs.front()->open;
|
||||
stats.close = recs.back()->close;
|
||||
stats.first_ts = recs.front()->timestamp;
|
||||
stats.last_ts = recs.back()->timestamp;
|
||||
|
||||
for (const auto* r : recs) {
|
||||
stats.low = std::min(stats.low, r->low);
|
||||
stats.high = std::max(stats.high, r->high);
|
||||
}
|
||||
|
||||
stats.avg = (stats.low + stats.high) / 2.0;
|
||||
|
||||
stats.avg = acc.avg_sum / static_cast<double>(acc.count);
|
||||
stats.open_min = acc.open_min;
|
||||
stats.open_max = acc.open_max;
|
||||
stats.close_min = acc.close_min;
|
||||
stats.close_max = acc.close_max;
|
||||
stats.count = acc.count;
|
||||
result.push_back(stats);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<DayStats> merge_day_stats(const std::vector<DayStats>& all_stats) {
|
||||
// Объединяем статистику по одинаковым дням (если такие есть)
|
||||
std::map<DayIndex, DayStats> merged;
|
||||
|
||||
for (const auto& s : all_stats) {
|
||||
auto it = merged.find(s.day);
|
||||
if (it == merged.end()) {
|
||||
merged[s.day] = s;
|
||||
} else {
|
||||
// Объединяем данные за один день
|
||||
auto& m = it->second;
|
||||
m.low = std::min(m.low, s.low);
|
||||
m.high = std::max(m.high, s.high);
|
||||
|
||||
// open берём от записи с меньшим timestamp
|
||||
if (s.first_ts < m.first_ts) {
|
||||
m.open = s.open;
|
||||
m.first_ts = s.first_ts;
|
||||
}
|
||||
|
||||
// close берём от записи с большим timestamp
|
||||
if (s.last_ts > m.last_ts) {
|
||||
m.close = s.close;
|
||||
m.last_ts = s.last_ts;
|
||||
}
|
||||
|
||||
m.avg = (m.low + m.high) / 2.0;
|
||||
}
|
||||
}
|
||||
|
||||
// Преобразуем в отсортированный вектор
|
||||
std::vector<DayStats> result;
|
||||
result.reserve(merged.size());
|
||||
|
||||
for (auto& [day, stats] : merged) {
|
||||
result.push_back(stats);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user