27std::string bar_prefix() {
29 if (!ctx.installed)
return std::string();
30 return "Error on bar " + std::to_string(ctx.bar_index) +
": ";
33[[noreturn]]
void length_not_positive(
long long value,
const char* function,
34 const char* argument) {
35 throw coded<std::runtime_error>(
37 {{
"function", function}, {
"argument", argument}, {
"rule",
"must_be_positive"}},
38 bar_prefix() +
"Invalid value of the '" + argument +
39 "' argument (" + std::to_string(value) +
") in the '" +
40 function +
"' function. It must be > 0.");
43[[noreturn]]
void length_na(
const char* function,
const char* argument) {
44 throw coded<std::runtime_error>(
46 {{
"function", function}, {
"argument", argument}, {
"rule",
"must_not_be_na"}},
47 bar_prefix() +
"Invalid value of the '" + argument +
48 "' argument in the '" + function +
49 "' function. It must not be na");
54bool length_value(
double value,
long long& out) {
55 if (
is_na(value))
return false;
56 const double t = std::trunc(value);
57 if (t > 1e15) out =
static_cast<long long>(1e15);
58 else if (t < -1e15) out =
static_cast<long long>(-1e15);
59 else out =
static_cast<long long>(
t);
63[[noreturn]]
void factor_not_positive(
double factor) {
65 if (factor == std::trunc(factor) && std::fabs(factor) < 1e15) {
66 std::snprintf(text,
sizeof text,
"%lld",
static_cast<long long>(factor));
68 std::snprintf(text,
sizeof text,
"%.15g", factor);
70 throw coded<std::runtime_error>(
72 {{
"function",
"supertrend"}, {
"argument",
"factor"}, {
"rule",
"must_be_positive"}},
73 bar_prefix() +
"Invalid value of the 'factor' argument (" +
text +
74 ") in the 'supertrend' function. It must be > 0.");
83 if (!length_value(value, length)) length_na(function, argument);
84 if (length <= 0) length_not_positive(length, function, argument);
85 return length > INT_MAX ? INT_MAX :
static_cast<int>(length);
89 if (
is_na(value)) length_na(function, argument);
90 if (value <= 0) length_not_positive(value, function, argument);
95 if (
is_na(value)) length_na(function, argument);
96 if (value <= 0) length_not_positive(value, function, argument);
97 return value > INT_MAX ? INT_MAX :
static_cast<int>(value);
102void HeldHistory::ensure_slots(std::size_t keep) {
105 const std::size_t chunks = keep /
kChunk + (keep %
kChunk != 0) + 1;
106 if (chunks > std::numeric_limits<std::size_t>::max() /
kChunk)
108 {{
"resource",
"layout"}},
"history byte extent is not representable");
110 if (slots <= values_.size())
return;
111 ::pineforge::detail::WindowValues<double> next(slots,
na<double>(), values_.cache_capacity());
112 for (
long long i = 0; i < count_; ++i)
113 next.write(
static_cast<std::size_t
>(i),
at(first_bar_ + i));
114 values_ = std::move(next);
118std::size_t HeldHistory::slot(std::size_t offset)
const {
119 const auto remaining = values_.size() - head_;
120 return offset >= remaining ? offset - remaining : head_ + offset;
123void HeldHistory::push(
double value) {
124 values_.write(slot(
static_cast<std::size_t
>(count_)), value);
128void HeldHistory::drop_front_chunks(std::size_t keep) {
129 while (count_ >
static_cast<long long>(
kChunk) &&
130 count_ -
static_cast<long long>(
kChunk) >=
static_cast<long long>(keep)) {
131 values_.retire_page(head_ /
kChunk);
133 count_ -=
static_cast<long long>(
kChunk);
134 first_bar_ +=
static_cast<long long>(
kChunk);
146 if (bar < last)
return;
148 values_.write(slot(
static_cast<std::size_t
>(count_ - 1)), value);
154 const double held =
at(last);
155 long long gap = bar - last - 1;
156 if (gap >=
static_cast<long long>(keep)) {
160 first_bar_ = bar -
static_cast<long long>(keep);
161 gap =
static_cast<long long>(keep);
163 for (
long long i = 0; i < gap; ++i) {
167 drop_front_chunks(keep);
170 drop_front_chunks(keep);
174 return values_.read(slot(
static_cast<std::size_t
>(bar - first_bar_)));
180 bool advance,
bool want_max,
181 const char* function) {
191 if (advance || !has_bar_) ++own_bar_;
196 if (length <= 0) length_not_positive(length, function,
"length");
202 "The script attempts to reference historical data that is too far from "
203 "the current bar (" + std::to_string(length - 1) +
204 " bars back). The historical buffer's limit is " +
209 if (!has_bar_ || bar != current_bar_) {
210 saved_state_ = state_;
214 state_ = saved_state_;
216 BarState& s = state_;
217 if (!s.started) first_exec_bar_ = bar;
218 history_.write(bar, src,
static_cast<std::size_t
>(
kMaxBarsBack) + 1);
220 const auto better = [want_max](
double a,
double b) {
return want_max ? a > b : a < b; };
223 const auto member = [&](
long long at,
double& value) ->
int {
224 if (at < origin)
return -1;
225 if (at < first_exec_bar_ || at < history_.first_bar())
return 0;
226 value = history_.at(at);
227 return is_na(value) ? 0 : 1;
230 const auto walk = [&](
long long from_k,
long long to_k, Cache& running) {
231 for (
long long k = from_k; k >= to_k; --k) {
233 const int kind = member(bar - k, v);
234 if (kind < 0)
continue;
236 running.valid =
false;
239 if (!running.valid || better(v, running.value)) {
240 running.valid =
true;
242 running.bar = bar - k;
246 const auto rescan = [&]() {
248 walk(length - 1, 0, fresh);
253 bool na_source =
false;
255 s.cache.valid =
false;
258 }
else if (!s.started) {
261 }
else if (length > s.prev_length) {
262 if (first_exec_bar_ > origin && length > s.max_length) s.cache.valid =
false;
263 if (!s.cache.valid || bar - s.cache.bar < length) {
264 walk(length - 1, s.prev_length, s.cache);
266 if (!s.cache.valid) {
270 }
else if (better(src, s.cache.value)) {
273 }
else if (bar - s.cache.bar >= length) {
277 if (!s.cache.valid) {
279 }
else if (better(src, s.cache.value)) {
282 }
else if (bar - s.cache.bar >= length) {
286 s.prev_length = length;
287 s.max_length = std::max(s.max_length, length);
290 const Cache& result = answer.valid ? answer : s.cache;
292 return Result{result.value, -
static_cast<double>(bar - result.bar)};
297long long series_length(
double length,
const char* function) {
300 if (!length_value(length, value)) length_not_positive(0, function,
"length");
307 return core_.update(src, series_length(length,
"highest"),
true,
true,
"highest").value;
310 return core_.update(src, series_length(length,
"highest"),
false,
true,
"highest").value;
313 return core_.update(src, series_length(length,
"lowest"),
true,
false,
"lowest").value;
316 return core_.update(src, series_length(length,
"lowest"),
false,
false,
"lowest").value;
319 return core_.update(src, series_length(length,
"highestbars"),
true,
true,
"highestbars")
323 return core_.update(src, series_length(length,
"highestbars"),
false,
true,
"highestbars")
327 return core_.update(src, series_length(length,
"lowestbars"),
true,
false,
"lowestbars")
331 return core_.update(src, series_length(length,
"lowestbars"),
false,
false,
"lowestbars")
339 if (again) atr_->recompute(high, low, close);
340 else atr_->compute(high, low, close);
343 const double atr = again ? atr_->recompute(high, low, close) : atr_->compute(high, low, close);
344 const double src = (high + low) / 2.0;
345 double upper = src + factor_ * atr;
346 double lower = src - factor_ * atr;
347 const double prev_lower =
is_na(state_.prev_lower) ? 0.0 : state_.prev_lower;
348 const double prev_upper =
is_na(state_.prev_upper) ? 0.0 : state_.prev_upper;
349 const double close1 = state_.prev_close;
351 lower = (lower > prev_lower || close1 < prev_lower) ? lower : prev_lower;
352 upper = (upper < prev_upper || close1 > prev_upper) ? upper : prev_upper;
354 if (
is_na(state_.prev_atr)) {
356 }
else if (state_.prev_st == prev_upper) {
357 direction = close > upper ? -1.0 : 1.0;
359 direction = close < lower ? 1.0 : -1.0;
361 const double line = direction == -1.0 ? lower : upper;
362 state_.prev_upper = upper;
363 state_.prev_lower = lower;
364 state_.prev_st = line;
365 state_.prev_atr = atr;
366 state_.prev_close = close;
367 return ta::SupertrendResult{line, direction};
371 double low,
double close) {
378 if (!
is_na(factor) && factor <= 0.0) factor_not_positive(factor);
382 saved_state_ = state_;
383 return step(high, low, close,
false);
387 double low,
double close) {
388 if (!latched_)
return compute(factor, atr_period, high, low, close);
389 state_ = saved_state_;
390 return step(high, low, close,
true);
Base with a code: throw coded<std::invalid_argument>(code, {args}, text).
long long last_bar() const
double at(long long bar) const
void write(long long bar, double value, std::size_t keep)
static constexpr std::size_t kChunk
ta::SupertrendResult recompute(double factor, double atr_period, double high, double low, double close)
ta::SupertrendResult compute(double factor, double atr_period, double high, double low, double close)
double recompute(double src, double length)
double compute(double src, double length)
double compute(double src, double length)
double recompute(double src, double length)
double recompute(double src, double length)
double compute(double src, double length)
double compute(double src, double length)
double recompute(double src, double length)
Result update(double src, long long length, bool advance, bool want_max, const char *function)
static constexpr long long kMaxBarsBack
std::uint64_t slots(std::uint64_t keys)
int simple_ta_length(double value, const char *function, const char *argument)
BarContext & bar_context()
@ scratch_capacity_exceeded
bool is_na(const Line &h)