algotradelib 0.0.0.9000
C++ header-only library for algorithmic trading
Loading...
Searching...
No Matches
series_buffer.hpp
1#pragma once
2
3#include "./conversion.hpp"
4#include "./frame.hpp"
5#include "./time_index.hpp"
6
7#include "../math/matrix.hpp"
8
9#include <chrono>
10#include <cstddef>
11#include <stdexcept>
12#include <vector>
13
14namespace algotradelib {
15namespace analytics {
16
31public:
34
42 SeriesBuffer(std::size_t capacity, std::size_t fields = bar_field_count)
43 : capacity_(capacity), fields_(fields) {
44 if (capacity == 0 || fields == 0) {
45 throw std::invalid_argument("a series buffer needs a capacity and at least one field");
46 }
47 // Twice the capacity bounds compaction to one row move per push, amortized.
48 values_ = math::unobserved(capacity * 2, fields);
49 stamps_.resize(capacity * 2);
50 }
51
56 std::size_t capacity() const noexcept { return capacity_; }
57
62 std::size_t fields() const noexcept { return fields_; }
63
68 std::size_t size() const noexcept { return count_; }
69
74 bool empty() const noexcept { return count_ == 0; }
75
80 bool full() const noexcept { return count_ == capacity_; }
81
86 std::size_t compactions() const noexcept { return compactions_; }
87
96 void push(Instant when, math::SeriesView row) {
97 if (static_cast<std::size_t>(row.size()) != fields_) {
98 throw std::invalid_argument("a series buffer observation must carry every field");
99 }
100 if (count_ > 0 && !(newest_instant() < when)) {
101 throw std::invalid_argument("a series buffer observation must be later than the last");
102 }
103
104 if (count_ == capacity_) {
105 ++begin_;
106 --count_;
107 }
108 if (begin_ + count_ == static_cast<std::size_t>(values_.rows())) {
109 compact();
110 }
111
112 const auto row_index = static_cast<math::Index>(begin_ + count_);
113 for (std::size_t field = 0; field < fields_; ++field) {
114 values_(row_index, static_cast<math::Index>(field)) =
115 row(static_cast<math::Index>(field));
116 }
117 stamps_[begin_ + count_] = when;
118 ++count_;
119 }
120
129 void push(Instant when, const std::vector<Scalar>& row) {
130 if (row.size() != fields_) {
131 throw std::invalid_argument("a series buffer observation must carry every field");
132 }
133 const math::Vector values = math::Vector::Map(row.data(), static_cast<math::Index>(row.size()));
134 push(when, values);
135 }
136
147 void push(const Bar& bar) {
148 if (fields_ != bar_field_count) {
149 throw std::invalid_argument("pushing a bar needs a buffer over every bar field");
150 }
152 if (bar.open_) {
153 row(static_cast<math::Index>(bar_field_index(BarField::OPEN))) = to_scalar(*bar.open_);
154 }
155 row(static_cast<math::Index>(bar_field_index(BarField::HIGH))) = to_scalar(bar.high_);
156 row(static_cast<math::Index>(bar_field_index(BarField::LOW))) = to_scalar(bar.low_);
157 row(static_cast<math::Index>(bar_field_index(BarField::CLOSE))) = to_scalar(bar.close_);
158 row(static_cast<math::Index>(bar_field_index(BarField::VOLUME))) = to_scalar(bar.volume_);
159 if (bar.traded_value_) {
160 row(static_cast<math::Index>(bar_field_index(BarField::TRADED_VALUE))) =
161 to_scalar(*bar.traded_value_);
162 }
163 push(bar.timestamp_, row);
164 }
165
174 math::SeriesView column(std::size_t field) const {
175 if (field >= fields_) {
176 throw std::out_of_range("series buffer field is past the end");
177 }
178 return values_.col(static_cast<math::Index>(field))
179 .segment(static_cast<math::Index>(begin_), static_cast<math::Index>(count_));
180 }
181
189 math::SeriesView column(BarField field) const { return column(bar_field_index(field)); }
190
199 Scalar at(std::size_t position, std::size_t field) const {
200 if (position >= count_ || field >= fields_) {
201 throw std::out_of_range("series buffer position is past the end");
202 }
203 return values_(
204 static_cast<math::Index>(begin_ + position), static_cast<math::Index>(field)
205 );
206 }
207
215 Instant instant(std::size_t position) const {
216 if (position >= count_) {
217 throw std::out_of_range("series buffer position is past the end");
218 }
219 return stamps_[begin_ + position];
220 }
221
228 if (count_ == 0) {
229 throw std::out_of_range("an empty series buffer has no newest observation");
230 }
231 return stamps_[begin_ + count_ - 1];
232 }
233
235 void clear() noexcept {
236 begin_ = 0;
237 count_ = 0;
238 }
239
240private:
241 // Moves the held observations to the front. Source and destination never overlap: this runs
242 // only when the held run reaches the end of storage, so begin_ = 2 * capacity - count_ >= count_.
243 void compact() {
244 const auto rows = static_cast<math::Index>(count_);
245 const auto from = static_cast<math::Index>(begin_);
246 values_.topRows(rows) = values_.middleRows(from, rows);
247 for (std::size_t position = 0; position < count_; ++position) {
248 stamps_[position] = stamps_[begin_ + position];
249 }
250 begin_ = 0;
251 ++compactions_;
252 }
253
254 std::size_t capacity_{0};
255 std::size_t fields_{0};
256 std::size_t begin_{0};
257 std::size_t count_{0};
258 std::size_t compactions_{0};
259 math::Matrix values_;
260 std::vector<Instant> stamps_;
261};
262
263} // namespace analytics
264} // namespace algotradelib
bool full() const noexcept
Tests whether the buffer holds capacity() observations.
Instant instant(std::size_t position) const
Returns the instant of one observation.
std::size_t size() const noexcept
Returns the number of observations held.
std::size_t compactions() const noexcept
Returns how many times the buffer moved its contents to the front of its storage.
math::SeriesView column(std::size_t field) const
Returns one field's values, oldest first.
void push(Instant when, const std::vector< Scalar > &row)
Appends one observation, dropping the oldest if the buffer is full.
std::size_t fields() const noexcept
Returns the number of values per observation.
math::SeriesView column(BarField field) const
Returns one bar field's values, oldest first.
bool empty() const noexcept
Tests whether the buffer is empty.
void push(Instant when, math::SeriesView row)
Appends one observation, dropping the oldest if the buffer is full.
SeriesBuffer(std::size_t capacity, std::size_t fields=bar_field_count)
Constructs an empty buffer.
void clear() noexcept
Discards every observation; the storage is kept.
TimeIndex::Instant Instant
The instant type observations are stamped with.
std::size_t capacity() const noexcept
Returns the most observations the buffer holds.
void push(const Bar &bar)
Appends one bar, dropping the oldest observation if the buffer is full.
Scalar at(std::size_t position, std::size_t field) const
Reads one value.
Instant newest_instant() const
Returns the instant of the newest observation.
std::chrono::system_clock::time_point Instant
The instant type of the index.
Conversions between exact Decimal64 values and the analytics Scalar.
The linear algebra types of the math module: Matrix, Vector, SeriesView, MutableSeriesView,...
Returns, rolling statistics, indicators and performance metrics over numeric series.
BarField
A bar field, used as a column of a SeriesFrame.
Definition frame.hpp:21
constexpr std::size_t bar_field_count
The number of BarField values, and so of columns in a SeriesFrame.
Definition frame.hpp:41
constexpr std::size_t bar_field_index(BarField field) noexcept
Returns the column position of a field in a SeriesFrame.
Definition frame.hpp:48
Scalar to_scalar(const Decimal64 &value)
Converts an exact decimal to the nearest Scalar.
double Scalar
The floating-point type the analytics and math layers compute in (double).
Definition scalar.hpp:18
Eigen::Matrix< Scalar, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor > Matrix
A dense, column-major matrix of Scalar.
Definition eigen.hpp:45
Eigen::Index Index
The signed index type of rows and columns.
Definition eigen.hpp:65
Vector unobserved(std::size_t count)
Makes a vector filled with missing().
Definition eigen.hpp:73
Eigen::Matrix< Scalar, Eigen::Dynamic, 1 > Vector
A dense column vector of Scalar.
Definition eigen.hpp:48
Eigen::Ref< const Vector > SeriesView
A read-only reference to one series.
Definition eigen.hpp:56
The library: broker-neutral types (core), and the modules below.
One bar: the prices and volume of one period, as exact decimals.