PineForge v0.13.1-379-g9b50973
Deterministic PineScript v6 backtest runtime — C ABI reference
Loading...
Searching...
No Matches
ta.hpp
Go to the documentation of this file.
1#pragma once
2#include "na.hpp"
3#include "series.hpp"
4#include "window_sum.hpp"
5#include <deque>
6#include <cmath>
7#include <vector>
8#include <string>
9
10namespace pineforge {
11
12namespace ta {
13
14// How an ``EMA`` seeds its recursion. A per-instance option
15// (``EMA(length, seeding)``); an instance that names none latches the
16// ambient default below on its first ``compute()``.
17enum class EmaSeeding : unsigned char {
18 // ``ema := src`` on the first finite input, so the output is never na.
19 // The default, and byte-identical to every EMA before the option existed.
21 // na until ``length`` finite inputs have accumulated, then their simple
22 // average, then the ordinary recursion: an SMA-seeded EMA, warming up the
23 // way ``RMA`` and ``SMA`` already do (na for the whole warm-up window).
25};
26
27// Ambient default seeding for ``EMA`` instances constructed without one
28// (thread-local; ``false`` = ``EmaSeeding::FirstValue``, ``true`` =
29// ``EmaSeeding::SimpleAverage``; default ``false``).
30//
31// An instance reads it once, on its first ``compute()``, and keeps that
32// seeding for life, so a host that wants the instances of one evaluation
33// context SMA-seeded without naming the seeding at each construction raises
34// the reference around that context and restores it on exit (an RAII scope;
35// the restore must also run during unwinding). Thread-local so parallel
36// in-process engines never cross-contaminate. Nothing in the kernel raises
37// it: with the default, every EMA that names no seeding is the first-value
38// recursion, byte-identical to the behaviour before the option existed.
40
41// Per-context bar index for bar-addressed window state (thread-local).
42//
43// TradingView keeps the source history of a window call site (``ta.highest``,
44// ``ta.lowest``, ``ta.highestbars``, ``ta.lowestbars``) in a ring of
45// ``K = length + 1`` slots addressed by ``bar_index % K``. A call on bar ``b``
46// writes ``slot[b % K] = src`` and returns the extremum over the WRITTEN slots
47// ``(b - k) % K``, ``k in [0, length)``. For a call that executes every bar
48// that is the plain positional window. For a call that executes
49// CONDITIONALLY (inside an ``if`` block that does not run every bar) the slots
50// not rewritten keep their stale values from earlier executions, never-written
51// slots are skipped, and the result is na iff ``bar_index < length - 1`` — NOT
52// until ``length`` executions have accumulated. Pinned 2026-09-03 with
53// ``lab tv`` on NYSE:F 1D (``if bar_index % 7 == 3: v = ta.highest(high, 5)``
54// 39/39 entry sizes exact, cadence 9 30/30; production: colasbreugnon-sma
55// F@1D 31/31 only with K = 6). ``ta.sma`` inside ``if`` is NOT ring-addressed
56// (per-execution buffer, pinned separately) and is untouched.
57//
58// The engine installs the executing context's bar index around every dispatch
59// of generated code: the chart's Pine ``bar_index`` around ``on_bar`` and the
60// requested context's evaluated-bar index around each ``evaluate_security``
61// (HTF / lower-tf / auxiliary feeds). ``origin`` is the first bar index the
62// context has data for — the warmup reference — so a feed that starts after
63// TradingView's hidden first chart bar (``bar_index_offset``) still warms up
64// over its own first ``length`` bars, exactly as before. With no context
65// installed (the default — unit tests, standalone use) every ``compute()`` is
66// its own bar and the objects are byte-identical to the previous positional
67// deque.
68//
69// The ring is read through a cached extremum with an implied bar. Each call
70// site keeps ``{value, bar}``: the first call caches ``(src, b)``; a call on
71// bar ``b`` with ``b - bar < length`` only compares ``src`` against the cache
72// (strict — ties never displace the cached member); the new-extremum test
73// comes FIRST, so a bar whose ``src`` beats the cache takes it without
74// reading the slots even when the cache has aged out; otherwise, once the
75// cached extremum has aged out (``b - bar >= length`` — by bars, not calls)
76// the slots are rescanned, oldest first, and the cache becomes
77// ``(best, b - k_best)``; otherwise the cache is kept. The output is the
78// cached value (the *Bars offset is ``b - bar``). Pinned 2026-09-04 with 8
79// ``lab tv`` tapes on NYSE:F 1D (696/696 entries), the BINANCE:ETHUSDT.P
80// geometry pin (82/82) and the jayentriken BBWP ETH replay (593/593):
81// ``m = bar_index % 49; if m < 4 or 37 <= m < 47 or m == 48: v =
82// ta.lowest(low, 10)`` answers 9.88 (cached from bar 42) on bars 48..51
83// although bar 3's stale 9.20 sits one slot behind bar 48 — the plain ring
84// would say 9.20 — and rescans to 9.20 on bar 52 once the cache has aged;
85// on ETH the run-start bar 4990 (low 2648.62 < the expired cache 2650.47)
86// answers its own low and never reads the aliased 08:15 slot the plain ring
87// returned. For an every-bar caller the cache is exactly the positional
88// window, so nothing changes there.
89//
90// Never-written slots read 0 in a rescan, for every source kind (pinned
91// 2026-09-04 on BINANCE:BTCUSDT 1D and OANDA:XAUUSD 1D, cadence 13 / length
92// 10: ``low``, ``close - 2000``, ``sma(close, 5)``, ``low[1]``, ``hl2`` and
93// the sign-mixed ``sma(close, 5) - close`` each 13/13). A slot WRITTEN with
94// na POISONS the ring (pinned 2026-09-04 on OANDA:XAUUSD 1D, scratchpad
95// r6/pins/ringkind: leading-na sites 7 x 13/13, the market-logic replica
96// 32/32; and the every-bar NYSE:F 1D gap tape pin-lowest-na-everybar, 20/20;
97// ledger log-20260904t112527z-ea13dfcd): the rescan walks oldest first and a
98// written-na slot resets the running extremum, so the answer is the extremum
99// over the slots NEWER than the newest na slot in the window (never-written
100// 0 slots among them included) and na when the newest slot is itself na; the
101// na write also poisons the cache to ``(na, b)``, so the next valid call
102// restarts from its own src without reading the slots. ``ta.lowest(
103// sma(close, 14), 10)`` at cadence 13 answers na (bar 5 writes na), 2998
104// (bar 18: restart on src, the never-written slot behind it unread), 0, 0,
105// 0, 0 (rescans: never-written slots newer than the na slot), 3357.6 (bar
106// 83: the na slot re-entered at k = 1, src is the only live member), 3327.4
107// (new minimum), 2998, 2998, 2998, 2998 (the stale first valid write, once
108// newer than the na slot), 3112. Every-bar ``x = bar_index % 4 == 0 ? na :
109// low``: ``ta.lowest(x, 5) == ta.highest(x, 5)`` on the bar after every na
110// bar — a single live member — where the old "skip the gap" reading kept
111// the pre-gap extremum (bar 9: TV 9.20, the untouched cache 8.44).
113 bool installed = false;
114 long long bar_index = 0; // ring address: the context's current bar index
115 long long origin = 0; // warmup reference: the context's first bar index
116};
118
119// RAII installer: sets the thread-local BarContext for the enclosed dispatch
120// and restores the previous one on exit (also during stack unwinding), so
121// chart and request.security evaluation never see each other's index.
123 BarContext previous_;
124
125public:
126 BarContextScope(long long bar_index, long long origin);
130};
131
132// Bar-addressed extremum ring shared by Highest / Lowest / HighestBars /
133// LowestBars — the TradingView rule documented at bar_context() above.
135public:
136 struct Result {
137 double value; // na: window not formed yet, or no written non-na member
138 int bars_back; // k of the extremum slot (0 = the current bar)
139 };
140 explicit ExtremeRing(int length);
141 // advance = true -> compute(): records src for the context's current bar
142 // advance = false -> recompute(): rewrites the current bar's slot
143 // An na src is WRITTEN: it answers na, poisons the cache and, in every
144 // later rescan, resets the running extremum (the poison rule documented
145 // at bar_context()). The composites (Stoch, WPR, Range) share the ring
146 // and the rule: their only oracle (finding 331, a stochRSI whose RSI is
147 // na for its warmup only) is a leading-na series, on which the poison
148 // answers exactly what "live over the non-na members" did.
149 // Ties resolve to the OLDEST member (strict comparison, oldest slot first).
150 Result update(double src, bool advance, bool want_max);
151
152private:
153 int length_;
154 std::vector<double> values_;
155 std::vector<unsigned char> written_;
156 long long own_bar_ = -1; // cadence when no context is installed
157 bool has_written_ = false;
158 // TradingView's cached extremum with an implied bar (bar_context() docs):
159 // the value the site last answered and the bar it is attributed to.
160 bool cached_ = false;
161 double cval_ = 0.0;
162 long long cbar_ = 0;
163 // The cache as it stood BEFORE the current bar's first tick, so a
164 // recompute() (same bar, advance=false) restores it and re-applies.
165 long long cache_seen_bar_ = -1;
166 bool saved_cached_ = false;
167 double saved_cval_ = 0.0;
168 long long saved_cbar_ = 0;
169};
170
171class RMA {
172 double output_val;
173 double sum;
174 int length;
175 int bar_count;
176
177 // saved state for recompute
178 double saved_output_val_;
179 double saved_sum_;
180 int saved_bar_count_;
181
182public:
183 explicit RMA(int length);
184 double compute(double src);
185 void save();
186 void restore();
187 double recompute(double src);
188};
189
190class RSI {
191 RMA rma_up;
192 RMA rma_down;
193 double prev_src;
194 int bar_count;
195
196 // saved state for recompute
197 double saved_prev_src_;
198 int saved_bar_count_;
199
200public:
201 explicit RSI(int length);
202 double compute(double src);
203 double recompute(double src);
204};
205
207 double prev_a;
208 double prev_b;
209
210 double saved_prev_a_, saved_prev_b_;
211
212public:
214 bool compute(double a, double b);
215 bool recompute(double a, double b);
216};
217
219 double prev_a;
220 double prev_b;
221
222 double saved_prev_a_, saved_prev_b_;
223
224public:
226 bool compute(double a, double b);
227 bool recompute(double a, double b);
228};
229
230// --- SMA ---
231
232class SMA {
233 KahanWindowSum window_;
234 int length;
235 int bar_count;
236
237public:
238 explicit SMA(int length);
239 double compute(double src);
240 double recompute(double src);
241};
242
243// --- EMA ---
244
245class EMA {
246 double output_val;
247 double alpha;
248 double sum;
249 int bar_count;
250 // Read only by EmaSeeding::SimpleAverage, which counts `length` finite
251 // inputs before seeding with their mean. The FirstValue recursion uses
252 // `alpha` alone and never reads this.
253 int length_;
254
255 // saved state for recompute
256 double saved_output_val_;
257 double saved_sum_;
258 int saved_bar_count_;
259
260 // The instance's seeding (true = EmaSeeding::SimpleAverage): named by the
261 // two-argument constructor, or latched from ema_na_warmup_flag() on the
262 // first compute() and never revisited, so recompute()'s restore() (which
263 // only rewinds output_val/sum/bar_count) cannot flip the seeding
264 // mid-series. Deliberately NOT part of save()/restore().
265 bool na_warmup_ = false;
266 bool warmup_latched_ = false;
267
268public:
269 explicit EMA(int length);
270 // Names the seeding at construction; the ambient default is never read.
271 EMA(int length, EmaSeeding seeding);
272 double compute(double src);
273 void save();
274 void restore();
275 double recompute(double src);
276};
277
278// --- MACD ---
279
281 double macd_line;
283 double histogram;
284};
285
286class MACD {
287 EMA fast_ema;
288 EMA slow_ema;
289 EMA signal_ema;
290
291public:
292 MACD(int fast_length, int slow_length, int signal_length);
293 MACDResult compute(double src);
295};
296
297// --- Highest ---
298
299class Highest {
300 ExtremeRing ring_;
301
302public:
303 explicit Highest(int length);
304 double compute(double src);
305 double recompute(double src);
306};
307
308// --- Lowest ---
309
310class Lowest {
311 ExtremeRing ring_;
312
313public:
314 explicit Lowest(int length);
315 double compute(double src);
316 double recompute(double src);
317};
318
319// --- ATR ---
320
321class ATR {
322 RMA rma;
323 double prev_close;
324 int bar_count;
325
326 double saved_prev_close_;
327 int saved_bar_count_;
328
329public:
330 explicit ATR(int length);
331 double compute(double high, double low, double close);
332 double recompute(double high, double low, double close);
333 // issue #178 (pinned 2026-09-06, lab tv i178-sparse-atr-sense,
334 // BINANCE:BTCUSDT 60, 398/398 executions): TradingView's ta.atr called
335 // inside a block that does not execute every bar advances its RMA on
336 // the EXECUTIONS only, but the true range always reads the previous
337 // CHART bar's close (close[1]), never the close of the previous
338 // execution. The 3-arg path keeps the per-call prev_close for callers
339 // that run every bar (identical there); a sparse call site must pass
340 // the chart's previous close (BacktestEngine::prev_chart_close()).
341 double compute(double high, double low, double close, double prev_chart_close);
342 double recompute(double high, double low, double close, double prev_chart_close);
343};
344
345// --- Stoch ---
346
347class Stoch {
348 Highest highest;
349 Lowest lowest;
350
351public:
352 explicit Stoch(int length);
353 double compute(double src, double high, double low);
354 double recompute(double src, double high, double low);
355};
356
357// --- Change ---
358
359class Change {
360 std::deque<double> history;
361 int max_length_;
362
363public:
364 explicit Change(int max_length = 1);
365 double compute(double src, int length = 1);
366 double recompute(double src, int length = 1);
367};
368
369// --- Cross ---
370
371class Cross {
372 double prev_a;
373 double prev_b;
374 // Skip-tie state: TV's `ta.cross` tracks the last NON-TIED sign of
375 // (a - b) so that intermediate "tied" bars (a == b) are transparent.
376 // ta.crossover/ta.crossunder use the simpler immediate-prev rule and
377 // need no skip-tie state.
378 int last_nonzero_sign_; // -1, 0 (uninitialised), +1
379
380 double saved_prev_a_, saved_prev_b_;
381 int saved_last_nonzero_sign_;
382
383public:
385 bool compute(double a, double b);
386 bool recompute(double a, double b);
387};
388
389// --- WMA (Weighted Moving Average) ---
390
391class WMA {
392 int length_;
393 // Ring buffer (capacity == length_) instead of std::deque to avoid
394 // per-bar node allocation. Newest sample at offset 0, oldest at
395 // offset length_-1. The per-bar O(length) weighted-sum recompute is
396 // unchanged — see WMA::compute for the oldest→newest weight 1..length
397 // accumulation order that parity depends on.
399
400public:
401 explicit WMA(int length);
402 double compute(double src);
403 double recompute(double src);
404};
405
406// --- HMA (Hull Moving Average) ---
407
408class HMA {
409 WMA wma_half_;
410 WMA wma_full_;
411 WMA wma_sqrt_;
412
413public:
414 explicit HMA(int length);
415 double compute(double src);
416 double recompute(double src);
417};
418
419// --- StdDev (Standard Deviation) ---
420
421// TradingView's ta.stdev (biased, the default) is NOT the two-pass
422// sum-of-squared-deviations of its documentation: it is
423// sqrt(Sxx / n - m * m), m = Sx / n,
424// with Sx = math.sum(src, n) and Sxx = math.sum(src * src, n) the same
425// sliding-window Kahan sums as ta.sma (window_sum.hpp) -- so the value carries
426// the cancellation error of that form and of those running sums. Pinned
427// 2026-09-05 (round 9, family W) on a full-precision NYSE:F@15 oracle
428// (pineforge-workflow famw-f15-sd-*: 7,029/7,029 bars bit-exact; the
429// two-pass form is exact on none of them). The unbiased variant
430// (biased = false) is unpinned and keeps the two-pass window arithmetic.
431class StdDev {
432 int length_;
433 bool biased_;
434 std::deque<double> buffer_; // unbiased path only
435 KahanWindowSum sum_x_; // Sx (biased path)
436 KahanWindowSum sum_xx_; // Sxx (biased path)
437 int bar_count_;
438
439public:
440 explicit StdDev(int length, bool biased = true);
441 double compute(double src);
442 double recompute(double src);
443
444private:
445 double held_stdev() const;
446 double biased_value() const;
447};
448
449// --- Supertrend ---
450
452 double value;
453 double direction;
454};
455
457 double factor_;
458 ATR atr_;
459 double prev_upper_, prev_lower_, prev_st_;
460 double prev_direction_;
461 double prev_close_;
462 bool initialized_;
463
464 // saved state
465 double saved_prev_upper_, saved_prev_lower_, saved_prev_st_;
466 double saved_prev_direction_, saved_prev_close_;
467 bool saved_initialized_;
468
469public:
470 Supertrend(double factor, int atr_period);
471 SupertrendResult compute(double high, double low, double close);
472 SupertrendResult recompute(double high, double low, double close);
473};
474
475// --- DMI (Directional Movement Index) ---
476
477struct DMIResult {
478 double diplus;
479 double diminus;
480 double adx;
481};
482
483class DMI {
484 RMA rma_plus_, rma_minus_, rma_tr_;
485 RMA rma_adx_;
486 double prev_high_, prev_low_, prev_close_;
487 bool first_bar_;
488
489 // saved state
490 double saved_prev_high_, saved_prev_low_, saved_prev_close_;
491 bool saved_first_bar_;
492
493public:
494 DMI(int di_length, int adx_smoothing);
495 DMIResult compute(double high, double low, double close);
496 DMIResult recompute(double high, double low, double close);
497};
498
499// --- SAR (Parabolic SAR) ---
500
501class SAR {
502 double start_, increment_, maximum_;
503 double af_, ep_, sar_;
504 bool is_long_;
505 bool initialized_;
506 double prev_high_, prev_low_, prev_close_;
507 double prev_prev_high_, prev_prev_low_;
508
509 // saved state
510 double saved_af_, saved_ep_, saved_sar_;
511 bool saved_is_long_, saved_initialized_;
512 double saved_prev_high_, saved_prev_low_, saved_prev_close_;
513 double saved_prev_prev_high_, saved_prev_prev_low_;
514
515public:
516 SAR(double start, double increment, double maximum);
517 double compute(double high, double low, double close);
518 double recompute(double high, double low, double close);
519};
520
521// --- BB (Bollinger Bands) ---
522
523struct BBResult {
524 double middle;
525 double upper;
526 double lower;
527};
528
529class BB {
530 double mult_;
531 SMA sma_;
532 StdDev stdev_;
533
534public:
535 BB(int length, double mult);
536 BBResult compute(double src);
537 BBResult recompute(double src);
538};
539
540// --- KC (Keltner Channels) ---
541
542struct KCResult {
543 double middle;
544 double upper;
545 double lower;
546};
547
548class KC {
549 double mult_;
550 EMA ema_;
551 EMA range_ema_;
552 double prev_close_ = na<double>();
553 double saved_prev_close_ = na<double>();
554
555public:
556 KC(int length, double mult);
557 KCResult compute(double src, double high, double low, double close);
558 KCResult recompute(double src, double high, double low, double close);
559};
560
561// --- PivotHigh ---
562
564 int left_bars_, right_bars_;
565 std::deque<double> buffer_;
566
567public:
568 PivotHigh(int left_bars, int right_bars);
569 double compute(double src);
570 double recompute(double src);
571};
572
573// --- PivotLow ---
574
575class PivotLow {
576 int left_bars_, right_bars_;
577 std::deque<double> buffer_;
578
579public:
580 PivotLow(int left_bars, int right_bars);
581 double compute(double src);
582 double recompute(double src);
583};
584
585// --- Linreg (Linear Regression) ---
586
587class Linreg {
588 int length_;
589 std::deque<double> buffer_;
590
591public:
592 explicit Linreg(int length);
593 double compute(double src, double offset);
594 double recompute(double src, double offset);
595};
596
597// --- PercentRank ---
598
600 int length_;
601 std::deque<double> buffer_;
602
603public:
604 explicit PercentRank(int length);
605 double compute(double src);
606 double recompute(double src);
607};
608
609// --- VWMA (Volume-Weighted Moving Average) ---
610
611class VWMA {
612 int length_;
613 std::deque<double> sv_buffer_;
614 std::deque<double> v_buffer_;
615 double sv_sum_, v_sum_;
616
617public:
618 explicit VWMA(int length);
619 double compute(double src, double vol);
620 double recompute(double src, double vol);
621};
622
623// --- Mom (Momentum) ---
624
625class Mom {
626 int length_;
627 std::deque<double> buffer_;
628
629public:
630 explicit Mom(int length);
631 double compute(double src);
632 double recompute(double src);
633};
634
635// --- ROC (Rate of Change) ---
636
637class ROC {
638 int length_;
639 std::deque<double> buffer_;
640
641public:
642 explicit ROC(int length);
643 double compute(double src);
644 double recompute(double src);
645};
646
647// --- Rising ---
648
649class Rising {
650 int length_;
651 std::deque<double> buffer_;
652
653public:
654 explicit Rising(int length);
655 double compute(double src);
656 double recompute(double src);
657};
658
659// --- Falling ---
660
661class Falling {
662 int length_;
663 std::deque<double> buffer_;
664
665public:
666 explicit Falling(int length);
667 double compute(double src);
668 double recompute(double src);
669};
670
671// --- CCI (Commodity Channel Index) ---
672
673class CCI {
674 int length_;
675 std::deque<double> buffer_;
676
677public:
678 explicit CCI(int length);
679 double compute(double src);
680 double recompute(double src);
681};
682
683// --- Cum (Cumulative Sum) ---
684
685class Cum {
686 double sum_;
687
688 double saved_sum_;
689
690public:
692 double compute(double src);
693 double recompute(double src);
694};
695
696// --- Chart all-time max/min of a series (ta.max / ta.min single-arg) ---
697
699 double max_;
700 bool has_;
701
702 double saved_max_;
703 bool saved_has_;
704
705public:
707 double compute(double src);
708 double recompute(double src);
709};
710
712 double min_;
713 bool has_;
714
715 double saved_min_;
716 bool saved_has_;
717
718public:
720 double compute(double src);
721 double recompute(double src);
722};
723
724// --- RCI (Rank Correlation Index, Spearman × 100) ---
725
726class RCI {
727 int length_;
728 std::deque<double> buffer_;
729
730public:
731 explicit RCI(int length);
732 double compute(double src);
733 double recompute(double src);
734};
735
736// --- Variance ---
737
738class Variance {
739 int length_;
740 bool biased_;
741 std::deque<double> buffer_;
742
743public:
744 explicit Variance(int length, bool biased = true);
745 double compute(double src);
746 double recompute(double src);
747};
748
749// --- Median ---
750
751class Median {
752 int length_;
753 std::deque<double> buffer_;
754
755public:
756 explicit Median(int length);
757 double compute(double src);
758 double recompute(double src);
759};
760
761// --- HighestBars ---
762
764 ExtremeRing ring_;
765
766public:
767 explicit HighestBars(int length);
768 double compute(double src);
769 double recompute(double src);
770};
771
772// --- LowestBars ---
773
775 ExtremeRing ring_;
776
777public:
778 explicit LowestBars(int length);
779 double compute(double src);
780 double recompute(double src);
781};
782
783// --- ALMA (Arnaud Legoux Moving Average) ---
784
785class ALMA {
786 int length_;
787 double offset_, sigma_;
788 std::deque<double> buffer_;
789
790public:
791 ALMA(int length, double offset = 0.85, double sigma = 6.0);
792 double compute(double src);
793 double recompute(double src);
794};
795
796// --- SWMA (Symmetrically Weighted Moving Average, period=4) ---
797
798class SWMA {
799 std::deque<double> buffer_;
800
801public:
803 double compute(double src);
804 double recompute(double src);
805};
806
807// --- MFI (Money Flow Index) ---
808
809class MFI {
810 int length_;
811 std::deque<double> pos_buffer_, neg_buffer_;
812 double prev_src_;
813 int bar_count_;
814
815 // saved state for recompute
816 double saved_prev_src_;
817 int saved_bar_count_;
818 std::deque<double> saved_pos_buffer_, saved_neg_buffer_;
819
820public:
821 explicit MFI(int length);
822 double compute(double src, double vol);
823 double recompute(double src, double vol);
824};
825
826// --- CMO (Chande Momentum Oscillator) ---
827
828class CMO {
829 int length_;
830 std::deque<double> up_buffer_, down_buffer_;
831 double prev_src_;
832 int bar_count_;
833
834 // saved state for recompute
835 double saved_prev_src_;
836 int saved_bar_count_;
837 std::deque<double> saved_up_buffer_, saved_down_buffer_;
838
839public:
840 explicit CMO(int length);
841 double compute(double src);
842 double recompute(double src);
843};
844
845// --- TSI (True Strength Index) ---
846
847class TSI {
848 EMA ema_long_;
849 EMA ema_short_;
850 EMA ema_abs_long_;
851 EMA ema_abs_short_;
852 double prev_src_;
853 int bar_count_;
854
855 double saved_prev_src_;
856 int saved_bar_count_;
857
858public:
859 TSI(int short_length, int long_length);
860 double compute(double src);
861 double recompute(double src);
862};
863
864// --- WPR (Williams %R) ---
865
866class WPR {
867 Highest highest_;
868 Lowest lowest_;
869
870public:
871 explicit WPR(int length);
872 double compute(double close, double high, double low);
873 double recompute(double close, double high, double low);
874};
875
876// --- COG (Center of Gravity) ---
877
878class COG {
879 int length_;
880 std::deque<double> buffer_;
881
882public:
883 explicit COG(int length);
884 double compute(double src);
885 double recompute(double src);
886};
887
888// --- BBW (Bollinger Bands Width) ---
889
890class BBW {
891 BB bb_;
892
893public:
894 BBW(int length, double mult);
895 double compute(double src);
896 double recompute(double src);
897};
898
899// --- KCW (Keltner Channel Width) ---
900
901class KCW {
902 KC kc_;
903
904public:
905 KCW(int length, double mult);
906 double compute(double src, double high, double low, double close);
907 double recompute(double src, double high, double low, double close);
908};
909
910// --- BarsSince ---
911
913 int count_;
914 bool ever_true_;
915
916 int saved_count_;
917 bool saved_ever_true_;
918
919public:
921 double compute(bool condition);
922 double recompute(bool condition);
923};
924
925// --- ValueWhen ---
926
928 std::deque<double> values_;
929 int max_occurrence_;
930
931 std::deque<double> saved_values_;
932
933public:
934 explicit ValueWhen(int max_occurrence = 1);
935 double compute(bool condition, double source, int occurrence);
936 double recompute(bool condition, double source, int occurrence);
937};
938
939// --- Correlation ---
940
942 int length_;
943 std::deque<double> x_buffer_, y_buffer_;
944
945public:
946 explicit Correlation(int length);
947 double compute(double src1, double src2);
948 double recompute(double src1, double src2);
949};
950
951// --- TR (True Range as function) ---
952
953class TR {
954 double prev_close_;
955 int bar_count_;
956 bool handle_na_;
957
958 double saved_prev_close_;
959 int saved_bar_count_;
960
961public:
962 explicit TR(bool handle_na = false);
963 double compute(double high, double low, double close);
964 double recompute(double high, double low, double close);
965 // issue #178: the sparse-call form — true range against the previous
966 // CHART bar's close (na on the first chart bar: handle_na decides).
967 double compute(double high, double low, double close, double prev_chart_close);
968 double recompute(double high, double low, double close, double prev_chart_close);
969};
970
971// --- PercentileNearestRank ---
972
974 int length_;
975 std::deque<double> buffer_;
976
977public:
978 explicit PercentileNearestRank(int length);
979 double compute(double src, double percentage);
980 double recompute(double src, double percentage);
981};
982
983// --- PercentileLinearInterpolation ---
984
986 int length_;
987 std::deque<double> buffer_;
988
989public:
990 explicit PercentileLinearInterpolation(int length);
991 double compute(double src, double percentage);
992 double recompute(double src, double percentage);
993};
994
995// --- Volume indicators ---
996class OBV {
997 double sum_ = 0.0;
998 double prev_close_ = na<double>();
999 int bar_count_ = 0;
1000
1001 // Mirror the initial committed state (see RMA::RMA) so a recompute()
1002 // before the first compute() restores a well-defined pristine state.
1003 double saved_sum_ = 0.0, saved_prev_close_ = na<double>();
1004 int saved_bar_count_ = 0;
1005
1006public:
1007 OBV() = default;
1008 double compute(double close, double volume);
1009 double recompute(double close, double volume);
1010};
1011
1012class AccDist {
1013 double sum_ = 0.0;
1014
1015 // Mirror the initial committed sum_ (see RMA::RMA) so a recompute()
1016 // before the first compute() restores a well-defined pristine state.
1017 double saved_sum_ = 0.0;
1018
1019public:
1020 AccDist() = default;
1021 double compute(double high, double low, double close, double volume);
1022 double recompute(double high, double low, double close, double volume);
1023};
1024
1025class NVI {
1026 double nvi_ = 1.0;
1027 double prev_close_ = na<double>();
1028 double prev_volume_ = na<double>();
1029 int bar_count_ = 0;
1030
1031 // Mirror the initial committed state (see RMA::RMA) so a recompute()
1032 // before the first compute() restores a well-defined pristine state.
1033 double saved_nvi_ = 1.0, saved_prev_close_ = na<double>(),
1034 saved_prev_volume_ = na<double>();
1035 int saved_bar_count_ = 0;
1036
1037public:
1038 NVI() = default;
1039 double compute(double close, double volume);
1040 double recompute(double close, double volume);
1041};
1042
1043class PVI {
1044 double pvi_ = 1.0;
1045 double prev_close_ = na<double>();
1046 double prev_volume_ = na<double>();
1047 int bar_count_ = 0;
1048
1049 // Mirror the initial committed state (see RMA::RMA) so a recompute()
1050 // before the first compute() restores a well-defined pristine state.
1051 double saved_pvi_ = 1.0, saved_prev_close_ = na<double>(),
1052 saved_prev_volume_ = na<double>();
1053 int saved_bar_count_ = 0;
1054
1055public:
1056 PVI() = default;
1057 double compute(double close, double volume);
1058 double recompute(double close, double volume);
1059};
1060
1061class PVT {
1062 double pvt_ = 0.0;
1063 double prev_close_ = na<double>();
1064
1065 // Mirror the initial committed state (see RMA::RMA) so a recompute()
1066 // before the first compute() restores a well-defined pristine state.
1067 double saved_pvt_ = 0.0, saved_prev_close_ = na<double>();
1068
1069public:
1070 PVT() = default;
1071 double compute(double close, double volume);
1072 double recompute(double close, double volume);
1073};
1074
1075class WAD {
1076 double wad_ = 0.0;
1077 double prev_close_ = na<double>();
1078
1079 // Mirror the initial committed state (see RMA::RMA) so a recompute()
1080 // before the first compute() restores a well-defined pristine state.
1081 double saved_wad_ = 0.0, saved_prev_close_ = na<double>();
1082
1083public:
1084 WAD() = default;
1085 double compute(double high, double low, double close);
1086 double recompute(double high, double low, double close);
1087};
1088
1089class WVAD {
1090public:
1091 WVAD() = default;
1092 double compute(double open, double high, double low, double close, double volume);
1093 double recompute(double open, double high, double low, double close, double volume);
1094};
1095
1096class III {
1097public:
1098 III() = default;
1099 double compute(double high, double low, double close, double volume);
1100 double recompute(double high, double low, double close, double volume);
1101};
1102
1103// --- VWAP Bands result (3-tuple: vwap, upper_band, lower_band) ---
1105 double vwap;
1106 double upper;
1107 double lower;
1108};
1109
1110class VWAP {
1111 double cum_pv_ = 0.0;
1112 double cum_vol_ = 0.0;
1113 // Sum of (price^2 * volume) for running variance computation used by
1114 // compute_bands(). Variance = cum_pv_sq_ / cum_vol_ - mean^2.
1115 double cum_pv_sq_ = 0.0;
1116 // Anchor day index: the SESSION day of the bar (session_day_index —
1117 // timestamp_ms / 86_400_000 on a UTC/24x7 symbol, the 17:00-ET-keyed
1118 // trading day on forex, the 09:30 RTH day on equities). On the first
1119 // compute() call we record this from the bar timestamp; on every
1120 // subsequent compute() the cumulator is reset whenever the day index
1121 // advances. Pine v6 `ta.vwap(source)` defaults to a Daily anchor
1122 // (`anchor = timeframe.change("1D")` on the SYMBOL's daily bar); the
1123 // engine matches that when the codegen threads syminfo tz + session.
1124 int64_t anchor_day_ = std::numeric_limits<int64_t>::min();
1125
1126 // Mirror the initial committed state (see RMA::RMA) so a recompute()
1127 // before the first compute() restores a well-defined pristine state.
1128 double saved_cum_pv_ = 0.0, saved_cum_vol_ = 0.0, saved_cum_pv_sq_ = 0.0;
1129 int64_t saved_anchor_day_ = std::numeric_limits<int64_t>::min();
1130
1131 void roll_anchor(int64_t day);
1132
1133public:
1134 VWAP() = default;
1135 // tz-less forms key the anchor on the UTC day (the 24x7 corpus regime);
1136 // the tz/session forms key it on the symbol's session day and reduce to
1137 // the same integer math for tz="UTC" + empty/"24x7" session.
1138 // Feature macro PF_VWAP_HAS_SESSION_ANCHOR (below the class) lets
1139 // generated code compile the (tz, session) tail out on older engines.
1140 double compute(double src, double volume, int64_t timestamp_ms);
1141 double recompute(double src, double volume, int64_t timestamp_ms);
1142 double compute(double src, double volume, int64_t timestamp_ms,
1143 const std::string& tz, const std::string& session);
1144 double recompute(double src, double volume, int64_t timestamp_ms,
1145 const std::string& tz, const std::string& session);
1146 VWAPBandsResult compute_bands(double src, double volume, int64_t timestamp_ms, double stdev_mult);
1147 VWAPBandsResult recompute_bands(double src, double volume, int64_t timestamp_ms, double stdev_mult);
1148 VWAPBandsResult compute_bands(double src, double volume, int64_t timestamp_ms, double stdev_mult,
1149 const std::string& tz, const std::string& session);
1150 VWAPBandsResult recompute_bands(double src, double volume, int64_t timestamp_ms, double stdev_mult,
1151 const std::string& tz, const std::string& session);
1152};
1153
1154// Feature macro: the emitted prelude's PF_VWAP_SESSION_ANCHOR_ARGS(tz, s)
1155// expands to `, tz, s` only when this is defined (see VWAP overloads above).
1156#define PF_VWAP_HAS_SESSION_ANCHOR 1
1157
1158// --- VWAP Bands wrapper class (3-tuple form: ta.vwap(src, anchor, stdev_mult)) ---
1159// Wraps VWAP and routes compute/recompute to compute_bands/recompute_bands with
1160// a fixed stdev_mult supplied at construction time. This lets the codegen use
1161// the standard .compute()/.recompute() dispatch pattern for tuple returns.
1163 VWAP vwap_;
1164 double stdev_mult_;
1165public:
1166 explicit VWAPBands(double stdev_mult) : stdev_mult_(stdev_mult) {}
1167 VWAPBandsResult compute(double src, double volume, int64_t timestamp_ms) {
1168 return vwap_.compute_bands(src, volume, timestamp_ms, stdev_mult_);
1169 }
1170 VWAPBandsResult recompute(double src, double volume, int64_t timestamp_ms) {
1171 return vwap_.recompute_bands(src, volume, timestamp_ms, stdev_mult_);
1172 }
1173 VWAPBandsResult compute(double src, double volume, int64_t timestamp_ms,
1174 const std::string& tz, const std::string& session) {
1175 return vwap_.compute_bands(src, volume, timestamp_ms, stdev_mult_, tz, session);
1176 }
1177 VWAPBandsResult recompute(double src, double volume, int64_t timestamp_ms,
1178 const std::string& tz, const std::string& session) {
1179 return vwap_.recompute_bands(src, volume, timestamp_ms, stdev_mult_, tz, session);
1180 }
1181};
1182
1183// --- Statistical ---
1184class Mode {
1185 int length_;
1186 std::deque<double> buffer_;
1187public:
1188 explicit Mode(int length) : length_(length) {}
1189 double compute(double src);
1190 double recompute(double src);
1191};
1192
1193class Range {
1194 Highest highest_;
1195 Lowest lowest_;
1196public:
1197 explicit Range(int length) : highest_(length), lowest_(length) {}
1198 double compute(double src);
1199 double recompute(double src);
1200};
1201
1202class Dev {
1203 int length_;
1204 std::deque<double> buffer_;
1205public:
1206 explicit Dev(int length) : length_(length) {}
1207 double compute(double src);
1208 double recompute(double src);
1209};
1210
1211// --- pivot_point_levels (free function) ---
1212
1213std::vector<double> pivot_point_levels(const std::string& method,
1214 double high, double low, double close);
1215
1216} // namespace ta
1217
1218} // namespace pineforge
double compute(double src)
double recompute(double src)
ALMA(int length, double offset=0.85, double sigma=6.0)
double recompute(double high, double low, double close)
double recompute(double high, double low, double close, double prev_chart_close)
double compute(double high, double low, double close)
double compute(double high, double low, double close, double prev_chart_close)
double compute(double high, double low, double close, double volume)
double recompute(double high, double low, double close, double volume)
double recompute(double src)
double compute(double src)
double recompute(double src)
double compute(double src)
double compute(double src)
double recompute(double src)
BBW(int length, double mult)
BBResult recompute(double src)
BB(int length, double mult)
BBResult compute(double src)
BarContextScope(const BarContextScope &)=delete
BarContextScope & operator=(const BarContextScope &)=delete
BarContextScope(long long bar_index, long long origin)
double compute(bool condition)
double recompute(bool condition)
double compute(double src)
double recompute(double src)
double recompute(double src)
double compute(double src)
double recompute(double src)
double compute(double src)
double compute(double src, int length=1)
double recompute(double src, int length=1)
Change(int max_length=1)
double compute(double src1, double src2)
double recompute(double src1, double src2)
bool compute(double a, double b)
bool recompute(double a, double b)
bool recompute(double a, double b)
bool compute(double a, double b)
bool recompute(double a, double b)
bool compute(double a, double b)
double compute(double src)
double recompute(double src)
DMI(int di_length, int adx_smoothing)
DMIResult compute(double high, double low, double close)
DMIResult recompute(double high, double low, double close)
Dev(int length)
Definition ta.hpp:1206
double recompute(double src)
double compute(double src)
double compute(double src)
EMA(int length, EmaSeeding seeding)
double recompute(double src)
Result update(double src, bool advance, bool want_max)
double recompute(double src)
double compute(double src)
double compute(double src)
double recompute(double src)
double compute(double src)
double recompute(double src)
double recompute(double src)
double compute(double src)
double recompute(double high, double low, double close, double volume)
double compute(double high, double low, double close, double volume)
double recompute(double src, double high, double low, double close)
KCW(int length, double mult)
double compute(double src, double high, double low, double close)
KCResult compute(double src, double high, double low, double close)
KCResult recompute(double src, double high, double low, double close)
KC(int length, double mult)
double recompute(double src, double offset)
double compute(double src, double offset)
double recompute(double src)
double compute(double src)
double compute(double src)
double recompute(double src)
MACDResult recompute(double src)
MACD(int fast_length, int slow_length, int signal_length)
MACDResult compute(double src)
double compute(double src, double vol)
double recompute(double src, double vol)
double recompute(double src)
double compute(double src)
double compute(double src)
double recompute(double src)
Mode(int length)
Definition ta.hpp:1188
double recompute(double src)
double compute(double src)
double recompute(double close, double volume)
double compute(double close, double volume)
double recompute(double close, double volume)
double compute(double close, double volume)
double recompute(double close, double volume)
double compute(double close, double volume)
double recompute(double close, double volume)
double compute(double close, double volume)
double compute(double src)
double recompute(double src)
double recompute(double src, double percentage)
double compute(double src, double percentage)
double recompute(double src, double percentage)
double compute(double src, double percentage)
double compute(double src)
PivotHigh(int left_bars, int right_bars)
double recompute(double src)
PivotLow(int left_bars, int right_bars)
double recompute(double src)
double compute(double src)
double recompute(double src)
double compute(double src)
double compute(double src)
double recompute(double src)
double recompute(double src)
double compute(double src)
double compute(double src)
double recompute(double src)
double compute(double src)
Range(int length)
Definition ta.hpp:1197
double recompute(double src)
double compute(double src)
double recompute(double src)
SAR(double start, double increment, double maximum)
double compute(double high, double low, double close)
double recompute(double high, double low, double close)
double recompute(double src)
double compute(double src)
double compute(double src)
double recompute(double src)
StdDev(int length, bool biased=true)
double recompute(double src)
double compute(double src)
double compute(double src, double high, double low)
double recompute(double src, double high, double low)
Supertrend(double factor, int atr_period)
SupertrendResult recompute(double high, double low, double close)
SupertrendResult compute(double high, double low, double close)
double compute(double high, double low, double close, double prev_chart_close)
TR(bool handle_na=false)
double recompute(double high, double low, double close, double prev_chart_close)
double recompute(double high, double low, double close)
double compute(double high, double low, double close)
double compute(double src)
TSI(int short_length, int long_length)
double recompute(double src)
VWAPBandsResult recompute(double src, double volume, int64_t timestamp_ms)
Definition ta.hpp:1170
VWAPBandsResult compute(double src, double volume, int64_t timestamp_ms)
Definition ta.hpp:1167
VWAPBands(double stdev_mult)
Definition ta.hpp:1166
VWAPBandsResult compute(double src, double volume, int64_t timestamp_ms, const std::string &tz, const std::string &session)
Definition ta.hpp:1173
VWAPBandsResult recompute(double src, double volume, int64_t timestamp_ms, const std::string &tz, const std::string &session)
Definition ta.hpp:1177
double recompute(double src, double volume, int64_t timestamp_ms)
double recompute(double src, double volume, int64_t timestamp_ms, const std::string &tz, const std::string &session)
VWAPBandsResult recompute_bands(double src, double volume, int64_t timestamp_ms, double stdev_mult)
double compute(double src, double volume, int64_t timestamp_ms, const std::string &tz, const std::string &session)
VWAPBandsResult recompute_bands(double src, double volume, int64_t timestamp_ms, double stdev_mult, const std::string &tz, const std::string &session)
VWAPBandsResult compute_bands(double src, double volume, int64_t timestamp_ms, double stdev_mult, const std::string &tz, const std::string &session)
double compute(double src, double volume, int64_t timestamp_ms)
VWAPBandsResult compute_bands(double src, double volume, int64_t timestamp_ms, double stdev_mult)
double compute(double src, double vol)
double recompute(double src, double vol)
double compute(bool condition, double source, int occurrence)
double recompute(bool condition, double source, int occurrence)
ValueWhen(int max_occurrence=1)
Variance(int length, bool biased=true)
double recompute(double src)
double compute(double src)
double compute(double high, double low, double close)
double recompute(double high, double low, double close)
double recompute(double src)
double compute(double src)
double compute(double close, double high, double low)
double recompute(double close, double high, double low)
double recompute(double open, double high, double low, double close, double volume)
double compute(double open, double high, double low, double close, double volume)
bool & ema_na_warmup_flag()
BarContext & bar_context()
std::vector< double > pivot_point_levels(const std::string &method, double high, double low, double close)
double na< double >()
Definition na.hpp:17