algotradelib 0.0.0.9000
C++ header-only library for algorithmic trading
Loading...
Searching...
No Matches
sizing.hpp
1#pragma once
2
3#include "./signal.hpp"
4
5#include "../event.hpp"
6#include "../intent.hpp"
7
8#include <algotradelib/analytics>
9#include <algotradelib/core>
10
11#include <cstddef>
12#include <string>
13#include <vector>
14
15namespace algotradelib {
16namespace strategy {
17namespace vanilla {
18
30 struct Params {
34 std::size_t window_{20};
40 std::string tag_{"volatility-target"};
41 };
42
45
50 std::size_t warmup_bars() const { return params_.window_ + 2; }
51
58 template <class Ctx>
59 void on_bar(Ctx& ctx, const BarEvent& bar) {
60 if (!(bar.instrument_ == params_.instrument_)) {
61 return;
62 }
63 if (params_.window_ < 2) {
64 return;
65 }
66 const auto* series = ctx.bars(bar.instrument_, bar.interval_);
67 if (series == nullptr) {
68 return;
69 }
70 const auto closes = series->column(analytics::BarField::CLOSE);
71 // One extra row, because a return consumes one: a deviation over `window_` returns needs
72 // `window_ + 1` closes.
73 const auto window = trailing(closes, params_.window_ + 1);
74 if (!window) {
75 return;
76 }
77
78 const auto period_returns = analytics::returns(*window);
79 const auto deviations = analytics::rolling_stdev(period_returns, params_.window_);
80 const auto deviation = deviations[deviations.size() - 1];
81 const auto close = (*window)[window->size() - 1];
82 if (!math::observed(deviation) || !math::observed(close)) {
83 return;
84 }
85 // A flat window has no volatility to size against; do not fall back to the maximum.
86 if (deviation <= math::Scalar{0} || close <= math::Scalar{0}) {
87 return;
88 }
89
90 const auto value_deviation = analytics::to_decimal(close * deviation, 4);
91 if (!value_deviation || value_deviation->coefficient() == 0) {
92 return;
93 }
94 const auto sized = decimal_divide(params_.risk_budget_, *value_deviation, 0);
95 if (!sized) {
96 return;
97 }
98
99 TargetPosition target;
101 target.target_quantity_ =
103 target.tag_ = params_.tag_;
104 ctx.submit(target);
105 }
106};
107
126 struct Params {
128 std::vector<InstrumentId> universe_;
130 std::uint8_t weight_scale_{4};
134 std::string tag_{"equal-weight"};
135 };
136
139
141 static constexpr std::size_t warmup_bars = 0;
142
149 template <class Ctx>
150 void on_phase(Ctx& ctx, MarketPhase phase) {
151 if (phase != params_.rebalance_at_ || params_.universe_.empty()) {
152 return;
153 }
154 // One divided by the count, truncated at the stated scale, so the arithmetic is exact and the
155 // sum is at most one rather than possibly over it.
156 const auto share = decimal_divide(
157 Decimal64::parse("1"),
158 Decimal64(static_cast<std::int64_t>(params_.universe_.size()), 0),
160 );
161 if (!share || share->coefficient() == 0) {
162 return;
163 }
164
165 for (const auto& instrument : params_.universe_) {
166 ctx.submit(
167 OrderIntent::market(instrument, OrderSide::BUY, SizeSpec::target_weight(*share))
168 .with_tag(params_.tag_)
169 );
170 }
171 }
172};
173
174} // namespace vanilla
175} // namespace strategy
176} // namespace algotradelib
An exact signed decimal: an int64 coefficient times 10^-scale.
Definition types.hpp:297
Decimal64()=default
Constructs zero.
static Decimal64 parse(std::string_view input)
Parses a decimal string exactly (no binary floating point).
Definition types.hpp:323
static OrderIntent market(InstrumentId instrument, OrderSide side, SizeSpec size, TimeInForce time_in_force=TimeInForce::DAY)
Makes a market order, which carries no price.
Definition intent.hpp:322
static SizeSpec target_weight(Decimal64 fraction)
Makes a size as a fraction of portfolio value to hold.
Definition intent.hpp:187
The events a runner delivers to a strategy's handlers, and the market phase.
What a strategy asks the runner for: orders (OrderIntent), target positions (TargetPosition) and canc...
math::Vector rolling_stdev(math::SeriesView values, std::size_t window, std::size_t min_observed)
Computes the trailing sample standard deviation (divisor count - 1).
Definition rolling.hpp:211
math::Vector returns(math::SeriesView prices)
Computes simple period returns, p[i] / p[i - 1] - 1.
Definition returns.hpp:27
std::optional< Decimal64 > to_decimal(Scalar value, std::uint8_t scale)
Converts a Scalar to an exact decimal with a given number of decimal places.
double Scalar
The floating-point type the analytics and math layers compute in (double).
Definition scalar.hpp:18
bool observed(Scalar value)
Tests whether a value is an observation.
Definition scalar.hpp:36
The strategies this library ships (buy and hold, crossovers, mean reversion, sizing,...
std::optional< math::SeriesView > trailing(math::SeriesView values, std::size_t count)
Returns the last count + 1 rows of a series.
Definition signal.hpp:145
What a strategy sees and emits: events, intents, the portfolio, reconciliation and risk limits.
Definition concept.hpp:16
MarketPhase
Where the clock stands relative to the trading session.
Definition event.hpp:30
@ OPEN
Inside the session.
Definition event.hpp:38
The library: broker-neutral types (core), and the modules below.
std::optional< Decimal64 > decimal_divide(const Decimal64 &lhs, const Decimal64 &rhs, std::uint8_t scale)
Divides two decimals, truncating toward zero at a given scale.
Definition types.hpp:602
bool decimal_less(const Decimal64 &lhs, const Decimal64 &rhs)
Compares two decimals numerically, whatever their scales.
Definition types.hpp:665
Helpers for the vanilla strategies that compute indicators.
Identifies an instrument: its country, venue and symbol.
Definition types.hpp:78
One bar, delivered to on_bar.
Definition event.hpp:202
The position a strategy wants to hold in one instrument.
Definition intent.hpp:442
InstrumentId instrument_
The instrument.
Definition intent.hpp:444
std::string tag_
A label carried to the orders and their fills.
Definition intent.hpp:450
Decimal64 target_quantity_
The position to hold, signed: negative is short, and zero means flat.
Definition intent.hpp:446
std::vector< InstrumentId > universe_
The instruments to hold; an empty universe does nothing.
Definition sizing.hpp:128
std::uint8_t weight_scale_
Decimal places of each weight; 4 is a hundredth of a percent.
Definition sizing.hpp:130
MarketPhase rebalance_at_
The phase whose start triggers a rebalance.
Definition sizing.hpp:132
Holds an equal fraction of portfolio value in each instrument of a universe.
Definition sizing.hpp:124
void on_phase(Ctx &ctx, MarketPhase phase)
Submits an equal-weight buy intent for every instrument when phase is rebalance_at_.
Definition sizing.hpp:150
static constexpr std::size_t warmup_bars
No history is needed before the first live bar.
Definition sizing.hpp:141
Decimal64 risk_budget_
The value volatility per bar the position should have, in the instrument's currency.
Definition sizing.hpp:36
Decimal64 max_quantity_
The largest position to hold, whatever the arithmetic gives.
Definition sizing.hpp:38
InstrumentId instrument_
The instrument to hold; bars for any other instrument are ignored.
Definition sizing.hpp:32
std::size_t window_
The number of returns the volatility is measured over; below 2, nothing is traded.
Definition sizing.hpp:34
Holds a long position sized so its value volatility per bar matches a risk budget.
Definition sizing.hpp:28
std::size_t warmup_bars() const
Returns how many bars to load before the first live bar.
Definition sizing.hpp:50
void on_bar(Ctx &ctx, const BarEvent &bar)
States the volatility-sized target position on each bar of the instrument.
Definition sizing.hpp:59