algotradelib 0.0.0.9000
C++ header-only library for algorithmic trading
Loading...
Searching...
No Matches
panel.hpp
1#pragma once
2
3#include "./frame.hpp"
4#include "./time_index.hpp"
5
6#include "../math/matrix.hpp"
7
8#include <algorithm>
9#include <cstddef>
10#include <iterator>
11#include <optional>
12#include <stdexcept>
13#include <string_view>
14#include <utility>
15#include <vector>
16
17namespace algotradelib {
18namespace analytics {
19
49
55inline constexpr std::string_view alignment_name(Alignment alignment) noexcept {
56 switch (alignment) {
58 return "union_unobserved";
60 return "intersection";
62 return "forward_fill";
63 }
64 return "unknown";
65}
66
74class Panel {
75public:
77 Panel() = default;
78
83 std::size_t rows() const noexcept { return index_.size(); }
84
89 std::size_t columns() const noexcept { return instruments_.size(); }
90
95 const TimeIndex& time() const noexcept { return index_; }
96
101 const std::vector<InstrumentId>& instruments() const noexcept { return instruments_; }
102
107 BarField field() const noexcept { return field_; }
108
113 Alignment alignment() const noexcept { return alignment_; }
114
120 std::optional<std::size_t> find(const InstrumentId& instrument) const {
121 for (std::size_t column = 0; column < instruments_.size(); ++column) {
122 if (instruments_[column] == instrument) {
123 return column;
124 }
125 }
126 return std::nullopt;
127 }
128
135 math::SeriesView column(std::size_t position) const {
136 if (position >= instruments_.size()) {
137 throw std::out_of_range("panel column is past the end");
138 }
139 return values_.col(static_cast<math::Index>(position));
140 }
141
149 Scalar at(std::size_t row, std::size_t column) const {
150 if (row >= rows() || column >= columns()) {
151 throw std::out_of_range("panel position is past the end");
152 }
153 return values_(static_cast<math::Index>(row), static_cast<math::Index>(column));
154 }
155
160 math::MatrixView values() const { return values_; }
161
172 const std::vector<BarSeries>& series,
175 ) {
176 Panel panel;
177 panel.field_ = field;
178 panel.alignment_ = alignment;
179 if (series.empty()) {
180 return panel;
181 }
182
183 std::vector<SeriesFrame> frames;
184 frames.reserve(series.size());
185 for (const auto& one : series) {
186 frames.push_back(analytics::from_bars(one));
187 panel.instruments_.push_back(one.instrument_);
188 }
189
190 auto stamps = alignment == Alignment::INTERSECTION ?
191 shared_instants(frames) :
192 combined_instants(frames);
193 panel.index_ = TimeIndex(std::move(stamps));
194
195 panel.values_ = math::Matrix::Constant(
196 static_cast<math::Index>(panel.index_.size()),
197 static_cast<math::Index>(frames.size()),
198 missing()
199 );
200
201 for (std::size_t column = 0; column < frames.size(); ++column) {
202 fill_column(panel, column, frames[column], field, alignment);
203 }
204 return panel;
205 }
206
207private:
208 // Every instant any frame reported, ascending and deduplicated.
209 static std::vector<TimeIndex::Instant> combined_instants(const std::vector<SeriesFrame>& frames) {
210 std::size_t total = 0;
211 for (const auto& frame : frames) {
212 total += frame.time().size();
213 }
214 std::vector<TimeIndex::Instant> all;
215 all.reserve(total);
216 for (const auto& frame : frames) {
217 const auto& stamps = frame.time().stamps();
218 all.insert(all.end(), stamps.begin(), stamps.end());
219 }
220 std::sort(all.begin(), all.end());
221 all.erase(std::unique(all.begin(), all.end()), all.end());
222 return all;
223 }
224
225 // Only the instants every frame reported.
226 static std::vector<TimeIndex::Instant> shared_instants(const std::vector<SeriesFrame>& frames) {
227 std::vector<TimeIndex::Instant> shared = frames.front().time().stamps();
228 std::vector<TimeIndex::Instant> narrowed;
229 narrowed.reserve(shared.size());
230 for (std::size_t position = 1; position < frames.size(); ++position) {
231 const auto& other = frames[position].time().stamps();
232 narrowed.clear();
233 std::set_intersection(
234 shared.begin(), shared.end(), other.begin(), other.end(), std::back_inserter(narrowed)
235 );
236 shared.swap(narrowed);
237 }
238 return shared;
239 }
240
241 // Writes one instrument's column under the chosen alignment, walking both ascending indices
242 // together in one pass.
243 static void fill_column(
244 Panel& panel,
245 std::size_t column,
246 const SeriesFrame& frame,
249 ) {
250 const auto& target = panel.index_.stamps();
251 const auto& source = frame.time().stamps();
252 if (target.empty() || source.empty()) {
253 return;
254 }
255 const auto series = frame.column(field);
256 const bool carry = alignment == Alignment::FORWARD_FILL;
257
258 std::size_t at = 0;
259 for (std::size_t row = 0; row < target.size(); ++row) {
260 const auto& when = target[row];
261 while (at + 1 < source.size() && !(when < source[at + 1])) {
262 ++at;
263 }
264 // `source[at]` is now the latest instant at or before `when`, unless the source
265 // begins after it — in which case there is nothing observed yet, and carrying
266 // backwards would be inventing a price the instrument never had.
267 if (when < source[at]) {
268 continue;
269 }
270 if (!carry && source[at] != when) {
271 continue;
272 }
273 panel.values_(static_cast<math::Index>(row), static_cast<math::Index>(column)) =
274 series(static_cast<math::Index>(at));
275 }
276 }
277
278 std::vector<InstrumentId> instruments_;
279 TimeIndex index_;
280 math::Matrix values_;
281 BarField field_{BarField::CLOSE};
283};
284
285} // namespace analytics
286} // namespace algotradelib
math::SeriesView column(std::size_t position) const
Returns one instrument's column.
Definition panel.hpp:135
std::size_t columns() const noexcept
Returns the number of columns.
Definition panel.hpp:89
Scalar at(std::size_t row, std::size_t column) const
Reads one value.
Definition panel.hpp:149
Panel()=default
Constructs an empty panel.
const TimeIndex & time() const noexcept
Returns the instants of the rows.
Definition panel.hpp:95
std::optional< std::size_t > find(const InstrumentId &instrument) const
Finds an instrument's column.
Definition panel.hpp:120
static Panel from_bars(const std::vector< BarSeries > &series, BarField field, Alignment alignment)
Builds a panel of one bar field from several bar series.
Definition panel.hpp:171
math::MatrixView values() const
Returns all values.
Definition panel.hpp:160
Alignment alignment() const noexcept
Returns how the panel lined up its instruments.
Definition panel.hpp:113
const std::vector< InstrumentId > & instruments() const noexcept
Returns the instruments of the columns.
Definition panel.hpp:101
BarField field() const noexcept
Returns the bar field the panel holds.
Definition panel.hpp:107
std::size_t rows() const noexcept
Returns the number of rows.
Definition panel.hpp:83
The instants a frame's rows correspond to, strictly ascending.
TimeIndex()=default
Constructs an empty index.
std::size_t size() const noexcept
Returns the number of instants.
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::string_view alignment_name(Alignment alignment) noexcept
Returns a lowercase display name for an alignment, such as "intersection".
Definition panel.hpp:55
Scalar missing()
Returns the value that stands for "no observation"; see math::missing.
Definition scalar.hpp:28
SeriesFrame from_bars(const BarSeries &series)
Converts a bar series into a frame of Scalar columns.
Definition frame.hpp:228
Alignment
How Panel::from_bars lines up instruments whose bars fall on different instants.
Definition panel.hpp:25
@ FORWARD_FILL
Every instant any instrument reported; a gap repeats the instrument's last value.
Definition panel.hpp:47
@ INTERSECTION
Only the instants every instrument reported.
Definition panel.hpp:38
@ UNION_UNOBSERVED
Every instant any instrument reported; an instrument with no bar there is missing().
Definition panel.hpp:31
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
Eigen::Ref< const Matrix > MatrixView
A read-only reference to a whole matrix.
Definition eigen.hpp:62
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.
Identifies an instrument: its country, venue and symbol.
Definition types.hpp:78