PineForge v0.13.1-379-g9b50973
Deterministic PineScript v6 backtest runtime — C ABI reference
Loading...
Searching...
No Matches
series.hpp
Go to the documentation of this file.
1#pragma once
2#include <deque>
3#include <vector>
4#include <algorithm>
5#include "na.hpp"
6
7namespace pineforge {
8
9template<typename T>
11 std::vector<T> buffer_;
12 std::size_t head_ = 0;
13 std::size_t size_ = 0;
14 std::size_t capacity_ = 0;
15
16public:
17 explicit DynamicRingBuffer(std::size_t capacity)
18 : capacity_(capacity) {}
19
20 void push_front(T val) {
21 if (capacity_ == 0) return;
22 // Lazy alloc: many Series are declared but never pushed; defer the
23 // allocation to first use. Invariant: buffer_.empty() implies
24 // size_ == 0, so no other member can read an unmaterialized buffer.
25 if (buffer_.empty()) buffer_.resize(capacity_, na<T>());
26 if (size_ == 0) {
27 buffer_[0] = val;
28 size_ = 1;
29 head_ = 0;
30 } else {
31 head_ = (head_ == 0) ? capacity_ - 1 : head_ - 1;
32 buffer_[head_] = val;
33 if (size_ < capacity_) {
34 size_++;
35 }
36 }
37 }
38
39 void update_front(T val) {
40 if (size_ == 0) {
41 push_front(val);
42 } else {
43 buffer_[head_] = val;
44 }
45 }
46
47 T operator[](std::size_t offset) const {
48 if (offset >= size_ || capacity_ == 0) {
49 return na<T>();
50 }
51 // offset < size_ <= capacity_ and head_ < capacity_, so
52 // head_ + offset < 2 * capacity_: one conditional subtract
53 // replaces the hardware divide of `% capacity_`.
54 std::size_t idx = head_ + offset;
55 if (idx >= capacity_) idx -= capacity_;
56 return buffer_[idx];
57 }
58
59 std::size_t size() const { return size_; }
60 std::size_t capacity() const { return capacity_; }
61
62 void clear() {
63 head_ = 0;
64 size_ = 0;
65 std::fill(buffer_.begin(), buffer_.end(), na<T>());
66 }
67
68 void resize(std::size_t new_capacity) {
69 if (new_capacity == capacity_) return;
70 std::vector<T> new_buffer(new_capacity, na<T>());
71 std::size_t new_size = std::min(size_, new_capacity);
72 for (std::size_t i = 0; i < new_size; ++i) {
73 new_buffer[i] = (*this)[i];
74 }
75 buffer_ = std::move(new_buffer);
76 capacity_ = new_capacity;
77 head_ = 0;
78 size_ = new_size;
79 }
80};
81
82/*
83 * PineScript history indexing for transpiled series (close, open, user vars, etc.)
84 *
85 * Pine uses expr[k] with k = 0 on the current bar, k >= 1 meaning k bars into the past
86 * (close[1] = previous bar). Newest values live at the front of the deque; operator[](k)
87 * reads k steps into the past. Codegen emits the same k for _s_close[k] and Series vars.
88 *
89 * push() — new bar; update() — same bar (magnifier intrabar). Negative or out-of-range
90 * offsets return na (insufficient history).
91 */
92
93template<typename T>
94class Series {
96
97public:
98 explicit Series(int max_len = 500) : buf(max_len) {}
99
100 void push(T value) {
101 buf.push_front(value);
102 }
103
104 void update(T value) {
105 buf.update_front(value);
106 }
107
108 T operator[](int offset) const {
109 if (offset < 0) {
110 return na<T>();
111 }
112 return buf[static_cast<std::size_t>(offset)];
113 }
114
115 T current() const {
116 return (*this)[0];
117 }
118
119 int size() const {
120 return static_cast<int>(buf.size());
121 }
122
123 void clear() {
124 buf.clear();
125 }
126};
127
128} // namespace pineforge
DynamicRingBuffer(std::size_t capacity)
Definition series.hpp:17
T operator[](std::size_t offset) const
Definition series.hpp:47
std::size_t size() const
Definition series.hpp:59
std::size_t capacity() const
Definition series.hpp:60
void resize(std::size_t new_capacity)
Definition series.hpp:68
T operator[](int offset) const
Definition series.hpp:108
void push(T value)
Definition series.hpp:100
int size() const
Definition series.hpp:119
T current() const
Definition series.hpp:115
Series(int max_len=500)
Definition series.hpp:98
void update(T value)
Definition series.hpp:104
T na()
Definition na.hpp:12