Series arithmetic (lib/numerics): - Add, Sub, Mul, Div, Lag, reusing BiInputIndicatorBase at period=1 - Div publishes 0.0 (not NaN) on a zero denominator, since a non-finite ComputeError result permanently poisons the base's running sum - BiInputIndicatorBase gains Subscribe(a, b) implementing the strategy-primitives spec's time-join rule for event-chained two-input nodes Phase 0 contracts (lib/core): - TTradeContext, BracketFlags, ExitReason, TrendState, VolState - SignalConstants (DefaultEpsilon, TruthThreshold), OutOfRangePolicy - ConfirmOnClose: bar-confirmation wrapper (Section 8.6) Signal predicates (lib/signals/predicates), Phase 1: - Above, Below, Equal (ComparePredicateBase<TOp> generic comparison core) - InsideChannel, OutsideChannel, ChannelPosition (new MultiInputBase, 3-input time-join) - IsRising, IsFalling (exclude current bar, confirm-then-push window) - AtHighest, AtLowest (include current bar, rebuild-on-correction, mirrors channels/Dc) - Hysteresis, PercentDistance All new nodes ship with .cs, tests, and .md docs; indices and the StrategyPrimitives.Spec.md are updated with placement decisions and design notes. Full suite: 16730 tests, 0 failures, 0 warnings.
5.0 KiB
ISRISING: Rising Predicate
Pine Script's
ta.risingasks a deceptively simple question — has this value beaten everything it's seen in the last n bars — and the "excludes the current bar" detail is the whole implementation.
| Property | Value |
|---|---|
| Category | Signals (Predicates) |
| Inputs | Source (single series) |
| Parameters | n (lookback, must be >= 1) |
| Outputs | Single crisp series (IsRising) |
| Output range | {0.0, 1.0} while hot; NaN during warmup |
| Warmup | n + 1 bars |
| PineScript | ta.rising(source, n) |
- ISRISING tests whether the current value is strictly greater than the maximum of the n bars preceding it.
- Similar: AtHighest, IsFalling | Trading note: The breakout-of-recent-range test;
BecameTrue(IsRising(n))on a source is the rising edge equivalent of a Breakout trigger. - Part of the strategy-primitives signal algebra. The exact definition here —
current > max(previous n), current bar excluded, comparison strict — was confirmed against the "most common understanding" during the design of this predicate, superseding an earlier draft that read Pine'sta.risingas adjacent-pair monotonicity.
Mathematical Foundation
\text{IsRising}_t(n) = \begin{cases} 1.0 & \text{if } x_t > \max(x_{t-1}, \ldots, x_{t-n}) \\ 0.0 & \text{otherwise} \end{cases}
The window is the previous n bars — the current bar is never compared against itself. A tie (x_t equal to the prior maximum) is 0.0: the comparison is strict. Contrast with AtHighest, whose window includes the current bar and asks a different question ("is the current bar the maximum"), which by definition is always at least a tie for the maximum, never a strict-greater-than test against itself.
Warmup
NaN until n confirmed prior bars exist — n + 1 total bars observed (the first bar can never have n predecessors).
Implementation Details
Confirm-Then-Push: Why No Rebuild Is Needed on Correction
The window (backed by a MonotonicDeque, same data structure as numerics/Highest) holds only confirmed prior bars, never the currently forming one. A tick with isNew = true first checks whether the previous forming bar's now-settled value should join the window (it does, if one exists), pushing it in with MonotonicDeque.PushMax — then only after that does the new tick become the new pending value. A tick with isNew = false (a correction) only ever overwrites the pending value; it never touches the window, since the window was never told about the currently-forming bar in the first place.
This is the same confirm-then-push pattern as ConfirmOnClose, applied to a rolling window instead of a single value, and it means IsRising needs no RebuildMax-on-correction logic at all — unlike AtHighest, whose inclusive window does need to rebuild, because a correction there changes a value that is already inside the window.
Performance Profile
| Operation | Count | Notes |
|---|---|---|
| Deque push (amortized) | ≤1 | Only when a bar closes |
| Deque query | 1 | GetExtremum |
| Comparison | 1 | current > extremum |
| Total | O(1) amortized | Same complexity class as numerics/Highest |
Validation
| Test | Status |
|---|---|
| Warmup returns NaN for the first n bars | ✅ |
| n=1: strictly greater than the immediately preceding bar | ✅ |
| Tie returns false (strict comparison) | ✅ |
| Current bar is excluded from its own comparison | ✅ |
| Correction to the forming bar does not disturb the confirmed window | ✅ |
| NaN/Infinity substitution | ✅ |
| Batch/streaming parity | ✅ |
Common Pitfalls
- The current bar is never part of the comparison.
IsRising(n)compares against the previous n bars only; a value tying its own immediately preceding value is not "rising," and a run of n identical values followed by a higher one is rising on that last bar, not sooner. - Do not confuse with
AtHighest.IsRising(n)andAtHighest(n)ask genuinely different questions (exclusive vs. inclusive window) and use differentMonotonicDequecorrection strategies internally; they are not interchangeable even though both use "n" and a rolling maximum. - Warmup is
n + 1, notn. The first bar can never have n predecessors, so the node needs to observe n+1 bars total before its first non-NaN output.
Usage Examples
// "Higher high than the last 5 confirmed bars"
var rising5 = new IsRising(5);
// Chained from another indicator
var trendAccelerating = new IsRising(new Roc(price, 10), 3);
References
- TradingView Pine Script Reference:
ta.rising.