24namespace indicator_detail {
39 const auto span =
static_cast<Scalar>(period);
43 std::size_t count = 0;
44 for (
math::Index row = 0; row < values.size(); ++row) {
45 const Scalar value = values[row];
54 if (count == period) {
55 average = (seed + value) / span;
57 average = (average * (span -
Scalar{1}) + value) / span;
59 result[row] = average;
85 paired.left_ = left_parts.values_.cwiseProduct(paired.present_);
86 paired.right_ = right_parts.values_.cwiseProduct(paired.present_);
92 bool reported_{
false};
93 std::size_t count_{0};
99inline Pairing reduce_pair(
100 const Paired& paired,
106 const Scalar count = paired.present_.segment(first, length).sum();
107 result.count_ =
static_cast<std::size_t
>(count);
108 if (result.count_ < needed || result.count_ < 2) {
111 result.mean_left_ = paired.left_.segment(first, length).sum() / count;
112 result.mean_right_ = paired.right_.segment(first, length).sum() / count;
113 result.reported_ =
true;
125 return values.segment(first, length) - mean * present.segment(first, length);
135 if (left.size() != right.size()) {
136 throw std::invalid_argument(
"paired series must have the same number of rows");
157 std::size_t min_observed
159 rolling_detail::check(window, min_observed);
162 for (
math::Index row = 0; row < values.size(); ++row) {
166 const math::Index first = rolling_detail::window_start(row, window);
167 const auto spread = rolling_detail::dispersion(parts, first, row - first + 1, min_observed);
168 if (!spread.reported_) {
171 if (spread.deviation_ ==
Scalar{0} ||
172 constant_detail::rounded_constant(
173 spread.deviation_, spread.mean_, spread.count_, parts.values_, parts.present_, first, row - first + 1
181 result[row] = (values[row] - spread.mean_) / spread.deviation_;
195 return zscore(values, window, window);
228 std::size_t min_observed
230 rolling_detail::check(window, min_observed);
232 const auto rows =
static_cast<std::size_t
>(values.size());
234 for (
math::Index row = 0; row < values.size(); ++row) {
235 const math::Index first = rolling_detail::window_start(row, window);
236 const auto spread = rolling_detail::dispersion(parts, first, row - first + 1, min_observed);
237 if (!spread.reported_) {
240 bands.
middle_[row] = spread.mean_;
241 bands.
upper_[row] = spread.mean_ + multiplier * spread.deviation_;
242 bands.
lower_[row] = spread.mean_ - multiplier * spread.deviation_;
260 std::size_t window = 20,
263 return bollinger(values, window, multiplier, window);
281 throw std::invalid_argument(
"rsi period must be at least one row");
283 const auto rows =
static_cast<std::size_t
>(prices.size());
286 for (
math::Index row = 1; row < prices.size(); ++row) {
290 const Scalar change = prices[row] - prices[row - 1];
291 gains[row] = std::max(change,
Scalar{0});
292 losses[row] = std::max(-change,
Scalar{0});
295 const math::Vector average_gain = indicator_detail::wilder(gains, period);
296 const math::Vector average_loss = indicator_detail::wilder(losses, period);
299 for (
math::Index row = 0; row < prices.size(); ++row) {
303 if (average_loss[row] ==
Scalar{0}) {
307 const Scalar strength = average_gain[row] / average_loss[row];
330 indicator_detail::require_same_rows(highs, lows);
331 indicator_detail::require_same_rows(highs, closes);
334 for (
math::Index row = 0; row < highs.size(); ++row) {
338 Scalar range = highs[row] - lows[row];
339 if (row > 0 &&
observed(closes[row - 1])) {
340 const Scalar previous = closes[row - 1];
341 range = std::max(range, std::abs(highs[row] - previous));
342 range = std::max(range, std::abs(lows[row] - previous));
366 std::size_t period = 14
369 throw std::invalid_argument(
"atr period must be at least one bar");
371 return indicator_detail::wilder(
true_range(highs, lows, closes), period);
401 std::size_t fast = 12,
402 std::size_t slow = 26,
403 std::size_t signal_span = 9
406 throw std::invalid_argument(
"macd fast span must be shorter than the slow span");
411 const auto rows =
static_cast<std::size_t
>(values.size());
413 for (
math::Index row = 0; row < values.size(); ++row) {
415 lines.
macd_[row] = quick[row] - slower[row];
419 for (
math::Index row = 0; row < values.size(); ++row) {
445 std::size_t min_observed
447 indicator_detail::require_same_rows(left, right);
448 rolling_detail::check(window, min_observed);
449 const auto paired = indicator_detail::pair_series(left, right);
451 for (
math::Index row = 0; row < left.size(); ++row) {
452 const math::Index first = rolling_detail::window_start(row, window);
454 const auto reduced = indicator_detail::reduce_pair(paired, first, length, min_observed);
455 if (!reduced.reported_) {
458 const math::Vector left_centred = indicator_detail::centred(
459 paired.left_, paired.present_, first, length, reduced.mean_left_
461 const math::Vector right_centred = indicator_detail::centred(
462 paired.right_, paired.present_, first, length, reduced.mean_right_
464 result[row] = left_centred.dot(right_centred) /
static_cast<Scalar>(reduced.count_ - 1);
505 std::size_t min_observed
507 indicator_detail::require_same_rows(left, right);
508 rolling_detail::check(window, min_observed);
509 const auto paired = indicator_detail::pair_series(left, right);
511 for (
math::Index row = 0; row < left.size(); ++row) {
512 const math::Index first = rolling_detail::window_start(row, window);
514 const auto reduced = indicator_detail::reduce_pair(paired, first, length, min_observed);
515 if (!reduced.reported_) {
518 const math::Vector left_centred = indicator_detail::centred(
519 paired.left_, paired.present_, first, length, reduced.mean_left_
521 const math::Vector right_centred = indicator_detail::centred(
522 paired.right_, paired.present_, first, length, reduced.mean_right_
524 const Scalar squared_left = left_centred.squaredNorm();
525 const Scalar squared_right = right_centred.squaredNorm();
526 const auto spread = [&](
Scalar squared) {
527 return std::sqrt(squared /
static_cast<Scalar>(reduced.count_ - 1));
529 if (squared_left ==
Scalar{0} || squared_right ==
Scalar{0} ||
530 constant_detail::rounded_constant(
531 spread(squared_left), reduced.mean_left_, reduced.count_, paired.left_, paired.present_, first, length
533 constant_detail::rounded_constant(
534 spread(squared_right), reduced.mean_right_, reduced.count_, paired.right_, paired.present_, first, length
542 const Scalar product = squared_left * squared_right;
543 const Scalar denominator =
544 std::isnormal(product) ? std::sqrt(product) : std::sqrt(squared_left) * std::sqrt(squared_right);
545 result[row] = left_centred.dot(right_centred) / denominator;
585 std::size_t min_observed
587 indicator_detail::require_same_rows(asset, market);
588 rolling_detail::check(window, min_observed);
589 const auto paired = indicator_detail::pair_series(asset, market);
591 for (
math::Index row = 0; row < asset.size(); ++row) {
592 const math::Index first = rolling_detail::window_start(row, window);
594 const auto reduced = indicator_detail::reduce_pair(paired, first, length, min_observed);
595 if (!reduced.reported_) {
598 const math::Vector market_centred = indicator_detail::centred(
599 paired.right_, paired.present_, first, length, reduced.mean_right_
601 const Scalar squared_market = market_centred.squaredNorm();
602 if (squared_market ==
Scalar{0} ||
603 constant_detail::rounded_constant(
604 std::sqrt(squared_market /
static_cast<Scalar>(reduced.count_ - 1)),
616 const math::Vector asset_centred = indicator_detail::centred(
617 paired.left_, paired.present_, first, length, reduced.mean_left_
619 result[row] = asset_centred.dot(market_centred) / squared_market;
638 return beta(asset, market, window, window);
Returns, rolling statistics, indicators and performance metrics over numeric series.
bool observed(Scalar value)
Tests whether a value is an observation; see math::observed.
math::Vector beta(math::SeriesView asset, math::SeriesView market, std::size_t window, std::size_t min_observed)
Computes the trailing beta of an asset against a market, cov(asset, market) / var(market).
math::Vector rsi(math::SeriesView prices, std::size_t period=14)
Computes Wilder's relative strength index.
math::Vector zscore(math::SeriesView values, std::size_t window, std::size_t min_observed)
Computes how many trailing sample standard deviations each value lies from its trailing mean.
Scalar alpha_from_span(std::size_t span)
Converts an EMA span to its smoothing factor, 2 / (span + 1).
BollingerBands bollinger(math::SeriesView values, std::size_t window, Scalar multiplier, std::size_t min_observed)
Computes Bollinger bands: the trailing mean and bands multiplier sample standard deviations either si...
math::Vector rolling_cov(math::SeriesView left, math::SeriesView right, std::size_t window, std::size_t min_observed)
Computes the trailing sample covariance of two series (divisor count - 1).
MacdLines macd(math::SeriesView values, std::size_t fast=12, std::size_t slow=26, std::size_t signal_span=9)
Computes the MACD (moving-average convergence/divergence) lines.
math::Vector ema(math::SeriesView values, Scalar alpha, std::size_t min_observed=1)
Computes the exponentially weighted moving average of a series.
math::Vector true_range(math::SeriesView highs, math::SeriesView lows, math::SeriesView closes)
Computes each bar's true range, max(high - low, |high - prev close|, |low - prev close|).
math::Vector atr(math::SeriesView highs, math::SeriesView lows, math::SeriesView closes, std::size_t period=14)
Computes the average true range with Wilder's smoothing.
math::Vector rolling_corr(math::SeriesView left, math::SeriesView right, std::size_t window, std::size_t min_observed)
Computes the trailing Pearson correlation of two series.
Observed observed_parts(SeriesView values)
Splits a series into zero-filled values and a presence mask.
double Scalar
The floating-point type the analytics and math layers compute in (double).
Vector both_present(const Vector &left, const Vector &right)
Combines two presence masks: 1 only where both series are observed.
Eigen::Index Index
The signed index type of rows and columns.
Scalar undefined()
Returns the value that stands for "the data was there, but the statistic does not exist".
Vector unobserved(std::size_t count)
Makes a vector filled with missing().
Eigen::Matrix< Scalar, Eigen::Dynamic, 1 > Vector
A dense column vector of Scalar.
Eigen::Ref< const Vector > SeriesView
A read-only reference to one series.
The library: broker-neutral types (core), and the modules below.
Trailing-window statistics: sums, means, deviations, extremes and moving averages.
The three lines of Bollinger bands, each as long as the input.
math::Vector upper_
middle_ plus multiplier trailing sample standard deviations.
math::Vector lower_
middle_ minus multiplier trailing sample standard deviations.
math::Vector middle_
The trailing mean.
The three lines of a MACD, each as long as the input.
math::Vector macd_
The fast EMA minus the slow EMA.
math::Vector histogram_
macd_ minus signal_.
math::Vector signal_
The EMA of macd_.