SPB Git

spb/wp40_uqo

Public MIT
4.5 KB · 80 lines cpp
1#include "raid/data/returns.hpp"2#include "raid/data/resample.hpp"3#include "raid/core/error.hpp"4#include <cmath>5#include <limits>67namespace raid::data {89std::vector<f64> returns(std::span<const f64> p, ReturnKind kind) {10  std::vector<f64> r; if (p.size() < 2) return r; r.resize(p.size() - 1);11  for (usize i = 1; i < p.size(); ++i) {12    RAID_REQUIRE(p[i] > 0 && p[i - 1] > 0, data_quality, "returns: non-positive price");13    r[i - 1] = kind == ReturnKind::log ? std::log(p[i] / p[i - 1]) : p[i] / p[i - 1] - 1.0;14  }15  return r;16}1718IntradayReturns intraday_returns(const BarSeries& b, ReturnKind kind, bool use_open_first) {19  IntradayReturns out; const usize S = b.n_sessions();20  out.session_offsets.push_back(0); out.session_dates = b.session_dates;21  const f64 nan = std::numeric_limits<f64>::quiet_NaN();22  f64 prev_close = nan;23  auto ret = [&](f64 a, f64 c) { RAID_REQUIRE(a > 0 && c > 0, data_quality, "returns: non-positive price"); return kind == ReturnKind::log ? std::log(c / a) : c / a - 1.0; };24  for (usize k = 0; k < S; ++k) {25    const usize b0 = static_cast<usize>(b.session_offsets[k]), b1 = static_cast<usize>(b.session_offsets[k + 1]);26    if (use_open_first) out.r.push_back(ret(b.open[b0], b.close[b0]));27    for (usize i = b0 + 1; i < b1; ++i) out.r.push_back(ret(b.close[i - 1], b.close[i]));28    out.session_offsets.push_back(static_cast<i64>(out.r.size()));29    out.overnight.push_back(std::isnan(prev_close) ? nan : ret(prev_close, b.open[b0]));30    out.open_to_close.push_back(ret(b.open[b0], b.close[b1 - 1]));31    out.close_to_close.push_back(std::isnan(prev_close) ? nan : ret(prev_close, b.close[b1 - 1]));32    prev_close = b.close[b1 - 1];33  }34  return out;35}3637BarSeries aggregate(const BarSeries& bars, Interval target, const MarketCalendar& cal, SessionPolicy policy) {38  RAID_REQUIRE(to_duration(target).us > to_duration(bars.interval).us, invalid_argument, "aggregate: target must be coarser than source");39  BarSeries out; out.symbol = bars.symbol; out.instrument_id = bars.instrument_id; out.interval = target; out.adjustment = bars.adjustment;40  const i64 step = to_duration(target).us;41  for (usize k = 0; k < bars.n_sessions(); ++k) {42    const Date d = bars.session_dates[k];43    if (!cal.is_trading_day(d)) continue;44    const auto sb = cal.session(d);45    const Timestamp anchor = policy == SessionPolicy::regular ? sb.open_utc : sb.ext_open_utc;46    const Timestamp end = policy == SessionPolicy::regular ? sb.close_utc : sb.ext_close_utc;47    i64 cur_bin = -1; f64 o = 0, h = 0, l = 0, c = 0, v = 0; u32 fl = 0; Timestamp bin_t;48    auto flush = [&]() { if (cur_bin >= 0) { out.t_open.push_back(bin_t); out.session.push_back(d); out.open.push_back(o); out.high.push_back(h); out.low.push_back(l); out.close.push_back(c); out.volume.push_back(v); out.flags.push_back(fl); } };49    for (i64 i = bars.session_offsets[k]; i < bars.session_offsets[k + 1]; ++i) {50      const usize j = static_cast<usize>(i); const Timestamp t = bars.t_open[j];51      if (t < anchor) continue;52      if (policy == SessionPolicy::regular ? t > end : t >= end) continue;53      i64 bin = (t - anchor).us / step;54      if (target == Interval::d1) bin = 0;55      // The 16:00 closing print (t == close) forms its own bin, as in FinData's derived 5m/30m/1h datasets56      // (verified live 2026-11-02: 79 regular 5m bars per normal session, 43 on early closes).57      if (bin != cur_bin) { flush(); cur_bin = bin; bin_t = anchor + Duration(bin * step); o = bars.open[j]; h = bars.high[j]; l = bars.low[j]; c = bars.close[j]; v = bars.volume[j]; fl = bars.flags[j]; }58      else { h = std::max(h, bars.high[j]); l = std::min(l, bars.low[j]); c = bars.close[j]; v += bars.volume[j]; fl |= bars.flags[j]; }59    }60    flush();61  }62  out.index_sessions();63  return out;64}6566SampledPrices subsample(const BarSeries& bars, usize step, usize offset) {67  RAID_REQUIRE(step >= 1 && offset < step, invalid_argument, "subsample: need step >= 1 and offset < step");68  SampledPrices s; s.session_offsets.push_back(0); s.session_dates = bars.session_dates;69  for (usize k = 0; k < bars.n_sessions(); ++k) {70    const usize b0 = static_cast<usize>(bars.session_offsets[k]), b1 = static_cast<usize>(bars.session_offsets[k + 1]);71    for (usize i = b0 + offset; i < b1; i += step) s.p.push_back(bars.close[i]);72    // always include the last price of the session so the grid ends at the close73    if (b1 > b0 && ((b1 - 1 - b0) % step) != offset % step && (b1 - 1) >= b0 + offset) s.p.push_back(bars.close[b1 - 1]);74    s.session_offsets.push_back(static_cast<i64>(s.p.size()));75  }76  return s;77}7879}  // namespace raid::data80
Files · v0.1.0-wp40
Menu

Theme