Edgoro
Indicator · Signals and price structure

Pine Script ta.valuewhen and ta.barssince examples

Get a value from the last time a condition was true with ta.valuewhen() and count the bars since with ta.barssince() in Pine Script v6, plus a var alternative.

Short answer

ta.valuewhen(condition, source, 0) returns source as it was on the most recent bar where condition was true (1 for the occurrence before that), and ta.barssince(condition) returns how many bars ago that was, or na if it never happened. Call both on every bar, at the top level of the script.

The script

From the last bullish EMA cross: the close at the cross, the close at the cross before it, the bars elapsed, the move since, and the highest high reached. Paste it into a new indicator in the Pine Editor, then save and add it to the chart.

Pine Script v6
//@version=6
indicator("Since the last signal", overlay = true)

fastLen = input.int(20, "Fast EMA", minval = 1)
slowLen = input.int(50, "Slow EMA", minval = 2)

fast = ta.ema(close, fastLen)
slow = ta.ema(close, slowLen)
bullCross = ta.crossover(fast, slow)

// Close at the most recent cross, and at the one before it
float lastCrossClose = ta.valuewhen(bullCross, close, 0)
float prevCrossClose = ta.valuewhen(bullCross, close, 1)
// Bars since the most recent cross: 0 on the cross bar, na before the first one
int barsSince = ta.barssince(bullCross)

// Highest high since the cross, kept with var instead of a lookback
var float highSince = na
if bullCross
    highSince := high
else if not na(highSince)
    highSince := math.max(highSince, high)

float movePct     = (close - lastCrossClose) / lastCrossClose * 100
bool  higherCross = bullCross and lastCrossClose > prevCrossClose

plot(lastCrossClose, "Close at the last cross", color.blue, style = plot.style_stepline)
plot(highSince, "Highest high since the cross", color.teal, style = plot.style_stepline)
plotshape(bullCross, "Bullish cross", shape.triangleup, location.belowbar, higherCross ? color.teal : color.gray, size = size.small)
plot(barsSince, "Bars since the cross", display = display.data_window)
plot(movePct, "% move since the cross", display = display.data_window)

How it works

  • ta.valuewhen(bullCross, close, 0). On a cross bar it returns that bar's close; on later bars it keeps returning it until the next cross. Occurrence 1 is the cross before that, 2 the one before, and so on.
  • higherCross compares the two: on a cross bar, lastCrossClose > prevCrossClose means this cross happened at a higher price than the previous one, which colors the triangle.
  • ta.barssince(bullCross) is 0 on the cross bar and counts up afterwards. It's na until the first cross, so it's shown in the Data Window rather than tested directly.
  • highSince with var. The highest high since the cross could also be ta.highest(high, barsSince + 1), but a length that keeps growing makes the script read further and further back. A var variable reset on the cross and raised with math.max() needs no history at all; the max_bars_back example explains why that matters.
  • Data Window plots. display = display.data_window keeps the bars-since count and the move off the chart's scale while still showing them when you hover a bar.

Pitfalls

  • Calling them inside an if block. ta.valuewhen() and ta.barssince() track every bar's condition; inside if, they only see the bars where the block runs (should be called on each calculation). Assign them at the top level.
  • na before the first event. ta.barssince() returns na until the condition has happened once, and any comparison with na is false, so barsSince > 10 and barsSince <= 10 are both false at first. Decide which way na should count: na(barsSince) or barsSince > 10.
  • A number as the condition. ta.valuewhen(ph, ph, 0) with ph = ta.pivothigh(...) doesn't compile in v6, which no longer treats numbers as conditions (if condition must be bool). Write ta.valuewhen(not na(ph), ph, 0).
  • [1] isn't the previous occurrence. lastCrossClose[1] is the value on the previous bar, which is the same as lastCrossClose except on cross bars. To compare a cross with the one before it, use occurrence 1, as the script does.
  • Conditions from the live bar. If the condition can flicker during the bar, so can the value returned. Add barstate.isconfirmed to the condition when the value feeds alerts or orders.

Variations

Bars since a strategy's entry. Strategies don't need ta.barssince() for this: strategy.opentrades.entry_bar_index(0) is the bar index where the open trade was filled, so a time-based exit is one subtraction.

//@version=6
strategy("Exit after N bars", overlay = true, initial_capital = 10000, commission_type = strategy.commission.percent, commission_value = 0.05, slippage = 2)
holdBars = input.int(10, "Bars to hold", minval = 1)
fast = ta.ema(close, 20)
slow = ta.ema(close, 50)
bullCross = ta.crossover(fast, slow)
if bullCross and strategy.position_size == 0
    strategy.entry("Long", strategy.long)
// Bars since the entry filled, read from the open trade
if strategy.position_size > 0 and bar_index - strategy.opentrades.entry_bar_index(0) >= holdBars
    strategy.close("Long", comment = "Time exit")
plot(fast, "Fast EMA", color.teal)
plot(slow, "Slow EMA", color.orange)

Any series at the event. The source can be anything calculated at the top level: with atr = ta.atr(14), ta.valuewhen(bullCross, atr, 0) freezes the ATR of the cross bar, a common base for a stop that doesn't move.

Questions

How do I get the value of a variable when a condition was last true in Pine Script?

Use ta.valuewhen(condition, variable, 0) at the top level of the script. It returns the variable's value from the most recent bar where the condition was true, and keeps it until the condition is true again.

What does the occurrence parameter of ta.valuewhen mean?

Which past event to read: 0 is the most recent bar where the condition was true, 1 the one before it, 2 the one before that. Write it as a number.

Why does ta.barssince return na?

Because the condition hasn't been true yet on any bar the script has seen. That is common on the first bars of the chart or with rare conditions. Test na(x) before comparing, or use nz(x, value) to substitute a value.

Every script on this page was checked with Edgoro Pine Check (Pine Script v6 rules). Pine Check is a static checker, not TradingView's compiler: TradingView compiles the script when you save it in the Pine Editor. The scripts are examples to learn from, not trading systems. Trading involves substantial risk of loss and is not suitable for every investor. Edgoro is a software tool, not a broker or financial adviser: nothing it produces is investment advice, and past or backtested performance does not guarantee future results.

Need a different version of this script?

Describe the change in plain English and Edgoro writes the Pine Script v6 for you, checked with Pine Check. For example:

“After each bullish 20/50 EMA cross, show the close at the cross, the highest high since, the bars elapsed and the percentage move, and mark crosses that happen higher than the previous one.”