How to Get the Previous High in TradingView Pine Script?

Introduction to Identifying Previous Highs in Pine Script

Understanding the Importance of Previous Highs in Technical Analysis

Previous price highs represent significant levels where upward price movement historically paused or reversed. They serve as key reference points for traders, often acting as potential resistance levels or targets for breakout strategies. Identifying and utilizing these levels programmatically in Pine Script is fundamental for building robust technical indicators and automated trading strategies.

Overview of Pine Script and its Capabilities

Pine Script is TradingView’s proprietary programming language designed for writing technical analysis indicators and trading strategies. It is specifically tailored for financial data, providing built-in functions for accessing price data, time, volume, and other market information across various chart resolutions. Its vector-based syntax allows for efficient calculation over historical bars.

Setting the Stage: Defining ‘Previous High’ for Scripting Purposes

The term ‘previous high’ can have different interpretations. For scripting, it typically refers to:

  • The high of the immediately preceding bar (high[1]).
  • The highest high over a specific lookback period ending before the current bar.
  • The highest high since a particular event occurred (e.g., the last significant low, the start of a session, or a specific date).

In this article, we will primarily focus on retrieving the highest high over a defined lookback period before the current bar, as this is a common requirement for resistance or range identification.

Methods for Retrieving the Previous High

Pine Script offers several ways to achieve this, each with its strengths and typical use cases.

Using the highest() function with offset to find previous high

The highest() function is the most direct way to find the highest value of a series over a specified number of past bars. To get the highest high excluding the current bar, we use the history-referencing operator [] with an offset of 1.

The syntax is highest(source, length)[offset]. To find the highest high over the last length bars, before the current bar, we use highest(high, length)[1]. This calculates the highest value of the high series over the range [bar_index - length + 1, bar_index] and then retrieves that value from one bar ago.

pine
//@version=5
indicator("Previous High (highest function)", overlay=true)

lookback = input.int(20, "Lookback Period", minval=1)

// Calculate the highest high over the lookback period ending *before* the current bar
previousHigh = highest(high, lookback)[1]

// Plot the result
plot(previousHigh, "Previous High", color=color.blue, style=plot.style_line)

// Highlight the actual points where the highest high occurred for verification
// This helps understand which historical bar's high value is being plotted.
var float highestPoint = na
if bar_index >= lookback
    highestPoint := highest(high, lookback)[1]

    // Find the bar within the lookback window that corresponds to this highest high
    // This part is tricky because highest() returns the value, not the bar_index
    // We can iterate back to find the bar, but it's inefficient for plotting many points.
    // A simpler way is to plot a dot on the bar where the current highest occurred.
    // This simplified plotting approach plots the level, not the source bar itself.

This method is simple and efficient for a fixed lookback period.

Employing valuewhen() function to pinpoint the previous high condition

valuewhen(condition, source, occurrence) finds the value of source on the bar where condition was true for the occurrence-th time counting back from the current bar. While not directly designed to find the highest value in a range, it’s useful if you want the high of the last bar where a specific condition was met before the current bar.

For instance, you might want the high of the last bar where a new all-time high was made, or the high of the last bar that closed above a certain level.

To use valuewhen to find a previous highest high would be complex. You’d need a condition that evaluates to true only on the bar that is the peak within your window, which is not straightforward with valuewhen alone. A more common use is finding the high of the previous bar where a signal occurred.

pine
//@version=5
indicator("Previous High on Condition", overlay=true)

// Example condition: price closed above Simple Moving Average
length = input.int(14, "SMA Length", minval=1)
ma = ta.sma(close, length)
condition = close > ma

// Get the high of the bar where the condition was true for the previous (1st) occurrence
// Note: valuewhen finds the 'occurrence'-th value COUNTING BACKWARDS.
// occurrence = 0 is the last time it was true (may be current bar)
// occurrence = 1 is the time BEFORE the last time.
// We need the HIGH value of the BAR where the condition was true the last time before the current bar.
// This requires a slight adjustment: we need the high of the bar where condition[1] was true for the 0th occurrence.

var float previousConditionHigh = na
// Check if condition[1] is true, meaning the *previous* bar met the condition
if condition[1]
    // If the previous bar met the condition, the high of that bar is high[1]
    previousConditionHigh := high[1]
else
    // If the previous bar did not meet the condition, find the high of the last bar before the current one
    // where the condition was true. This is valuewhen(condition, high, 0)[1]
    // However, a simpler approach is often to just plot the last known value or use `na`.
    // Let's get the high of the *most recent* bar where the condition was true, excluding the current bar.
    // This is valuewhen(condition, high, 0) *but* we need to handle the case where condition is true on current bar.
    // A safer way is to look for the 1st previous occurrence:
    previousConditionHigh := valuewhen(condition, high, 1)

plot(previousConditionHigh, "High on Previous Condition", color=color.purple, style=plot.style_circles)

This function is less suitable for finding the absolute highest high in a range but excellent for finding data points related to specific historical events.

Leveraging for loops for iterative high identification (less efficient)

While Pine Script is optimized for its built-in vector functions, you can use for loops to iterate through bars and find the highest high manually. However, this is generally less efficient than using highest() and should be avoided for performance-critical code, especially when the lookback period is large.

pine
//@version=5
indicator("Previous High (for loop)", overlay=true)

lookback = input.int(20, "Lookback Period", minval=1)

var float previousHigh = na

// Calculate the highest high over the lookback period ending *before* the current bar
// Iterate from 1 to lookback to check historical bars
if bar_index >= lookback
    maxH = high[1]
    for i = 2 to lookback
        if high[i] > maxH
            maxH := high[i]
    previousHigh := maxH
else
    // Not enough bars yet to calculate the lookback window
    previousHigh := na

// Plot the result
plot(previousHigh, "Previous High (for loop)", color=color.red, style=plot.style_line)

Use for loops sparingly and only when the required logic cannot be efficiently expressed with built-in functions. The highest() approach is almost always preferred for finding the maximum value in a historical range.

Implementing the Code: Step-by-Step Guide

Let’s consolidate the preferred method (highest()) into a more complete example.

Writing a basic script to identify and plot the previous highest high

This involves defining the lookback period, calculating the previous high using highest()[1], and plotting it on the chart.

pine
//@version=5
indicator("Previous Highest High Level", overlay=true)

// --- Inputs ---
lookback = input.int(30, "Lookback Period (Excluding Current Bar)", minval=1)
lineColor = input.color(color.blue, "Line Color")
lineWidth = input.int(2, "Line Width", minval=1)
lineStyle = input.string("Solid", "Line Style", options=["Solid", "Dotted", "Dashed"])

// Convert string style to plot style enum
style = lineStyle == "Dotted" ? plot.style_circles :
      lineStyle == "Dashed" ? plot.style_cross : plot.style_line

// --- Calculation ---
// Calculate the highest high over the *previous* 'lookback' bars.
// highest(high, lookback) calculates the highest from bar_index - lookback + 1 up to bar_index.
// Using [1] takes the value from one bar ago, shifting the window back.
// The window for highest(high, lookback)[1] is from bar_index - lookback up to bar_index - 1.
previousHighestHigh = highest(high, lookback)[1]

// --- Plotting ---
// Plot the calculated level.
plot(previousHighestHigh, "Previous Highest High", color=lineColor, linewidth=lineWidth, style=style)

// --- Optional: Show the value on the chart's price scale ---
// This ensures the line value is visible on the right-hand price scale.
plot(previousHighestHigh, "Label", display=display.none) // Use display.none to avoid double plotting the line itself

This script provides a clear line showing the highest price reached in the specified lookback window ending on the previous bar. Note the index [1] is crucial for ensuring the current bar’s high does not influence the level being plotted on the current bar.

Adding conditions and filters: Finding the previous high within a specific timeframe

Sometimes, you need the previous high within a different timeframe than the chart you are viewing. Pine Script’s request.security() function is used for this.

request.security(symbol, resolution, expression) fetches data from a different symbol and/or resolution and evaluates an expression on that data.

To get the previous high on a higher timeframe (e.g., daily high on an hourly chart), you’d do something like this:

pine
//@version=5
indicator("HTF Previous High", overlay=true)

// --- Inputs ---
htf = input.timeframe("D", "Higher Timeframe")
lookback = input.int(1, "Lookback Period (HTF Bars)", minval=1)

// --- Calculation ---
// Define the expression to calculate on the higher timeframe.
// We want the highest high over the *previous* 'lookback' HTF bars.
// The [1] offset applies to the HTF bars.
htf_highest_high_expression = "highest(high, " + str.tostring(lookback) + ")[1]"

// Request the calculation from the higher timeframe
// Use syminfo.tickerid for the current chart symbol
previousHtfHigh = request.security(syminfo.tickerid, htf, htf_highest_high_expression, lookahead=barmerge.lookahead_on)

// --- Plotting ---
// The value from the higher timeframe will be repainted across the corresponding lower timeframe bars.
plot(previousHtfHigh, "Previous HTF High", color=color.fuchsia, linewidth=2)

This example fetches the highest high of the previous lookback higher timeframe bars and plots it. lookahead=barmerge.lookahead_on is often used with request.security when referencing historical data to ensure the value is available on the current bar without looking into the future.

Customizing the appearance: Highlighting the previous high on the chart

Beyond a simple line, you can use hline, line.new, or box.new to visually represent the previous high. hline is static and good for fixed levels, but plot is generally preferred for dynamic levels like the previous highest high over a moving window.

You can change the color, style, and thickness of the plotted line using plot function arguments as shown in the basic example above.

To highlight specific points or ranges, you might use plotchar or plotshape on bars where the high reached or exceeded the previous highest high level.

pine
//@version=5
indicator("Previous High with Highlight", overlay=true)

lookback = input.int(50, "Lookback Period", minval=1)

previousHighestHigh = highest(high, lookback)[1]

plot(previousHighestHigh, "Level", color=color.blue, linewidth=2)

// Highlight bars where the price touches or exceeds the previous highest high level
barsTouching = high >= previousHighestHigh[0] and previousHighestHigh[0] > close[1] // check for valid level and if high reached it

// Use plotshape to put a marker on bars where the price reached the level
plotshape(barsTouching, title="Touched Previous High", style=shape.circle, location=location.abovebar, color=color.red, size=size.small)

This adds a visual cue on the chart when the current price interacts with the previously identified high level.

Advanced Techniques and Considerations

Handling edge cases: What happens when there’s no previous high?

When the lookback period is greater than the number of available bars (bar_index), highest(source, length)[offset] will return na (Not Available). Pine Script functions like plot automatically handle na by not drawing anything. However, if you use the value in calculations, you might need to handle na explicitly using na() or providing a default value with nz(). For robust scripts, ensure calculations only proceed when bar_index is sufficient for the lookback window.

pine
//@version5
indicator("Previous High with NA Handling", overlay=true)

lookback = input.int(20, "Lookback Period", minval=1)

// Check if enough bars exist for the lookback calculation
float previousHigh = na
if bar_index >= lookback
    previousHigh := highest(high, lookback)[1]

// Now 'previousHigh' will be 'na' until bar_index is >= lookback
// You can use nz() if you need a default value instead of na for calculations:
// float previousHighNz = nz(previousHigh, close) // Use close as default, for example

plot(previousHigh, "Previous High (NA Handled)", color=color.green)

Optimizing script performance when searching for highs over long periods

Using highest(high, lookback)[1] is generally efficient. However, if you need the highest high since the beginning of time or over an extremely long, non-fixed period, calculating highest(high, bar_index)[1] on every bar might become less performant than necessary for very long histories. Pine Script’s execution engine is highly optimized for built-in functions, so trust highest. If you were implementing a similar logic with a for loop over a very long range, performance would degrade significantly.

For dynamic, event-driven periods (e.g., highest high since the last time price broke a certain level), you might need to track this value using var variables and update it conditionally, which can be more efficient than re-scanning a long range every bar.

pine
//@version=5
indicator("Highest High Since Last Low", overlay=true)

// Simple example: Track highest since a new N-bar low occurred
lookbackLow = input.int(10, "Lookback for Low", minval=2)

// Identify a new N-bar low
newLow = ta.lowest(low, lookbackLow) == low

var float highestSinceLow = na

// If a new low occurs OR if it's the first bar, reset the tracked high
if newLow or bar_index == 0
    highestSinceLow := high
else
    // Otherwise, update the tracked high if the current high is greater
    if high > highestSinceLow
        highestSinceLow := high

// Plot the highest high since the last 'newLow' event
plot(highestSinceLow, "Highest Since Last Low", color=color.orange)

This pattern, using var to maintain state, is very efficient for tracking values over non-fixed periods determined by chart events.

Combining previous high with other indicators for enhanced analysis

Previous highs are powerful when combined with other tools. You can use them with:

  • Volume: Look for increased volume on attempts to break the previous high.
  • Moving Averages: Check if the previous high coincides with a significant moving average level.
  • Oscillators (RSI, MACD): Look for divergence when price approaches a previous high.
  • Fibonacci Retracements/Extensions: Previous highs often align with key Fibonacci levels.
  • Support/Resistance: Previous highs are forms of historical resistance.

For example, you could plot the previous highest high and color it differently based on whether volume is increasing as price approaches it.

Potential pitfalls and common mistakes when coding in Pine Script

  • Off-by-one errors with history reference []: Remember [0] is the current bar, [1] is the previous bar, etc. When finding the high before the current bar over a lookback, the correct syntax is highest(source, length)[1]. Forgetting the [1] means your ‘previous’ high calculation includes the current bar’s data, which is a common lookahead error in trading logic.
  • Repainting with request.security: Be mindful of lookahead argument in request.security. Using lookahead=barmerge.lookahead_on is generally safe for historical data (expression depends only on past/present data). Avoid lookahead=barmerge.lookahead_off or using future-referencing code within the expression unless specifically intended, as it can cause values to change on historical bars as new data arrives.
  • Misunderstanding valuewhen occurrences: valuewhen(condition, source, 0) gives the most recent occurrence (which could be the current bar). valuewhen(condition, source, 1) gives the previous occurrence before the most recent one. If you need the high of the last bar before the current one where a condition was true, you often need to combine valuewhen with history references or check condition[1].
  • Performance with loops: As mentioned, for loops can be slow for iterating over long history compared to built-in functions.

Practical Examples and Applications

Using the previous high as a support/resistance level

The most common application is using the previous highest high over a relevant period (e.g., last 20-50 bars, previous session, previous week) as a dynamic resistance level. A break above this level can signal bullish continuation, while a rejection can reinforce its role as resistance.

pine
//@version=5
indicator("Resistance from Previous High", overlay=true)

lookback = input.int(50, "Resistance Lookback", minval=1)

// Calculate previous highest high
resistanceLevel = highest(high, lookback)[1]

// Plot the level
plot(resistanceLevel, "Resistance Level", color=color.red, linewidth=2, style=plot.style_dotted)

// Alerting on touch or break
resistanceTouched = ta.cross(high, resistanceLevel)
resistanceBroken = ta.cross(close, resistanceLevel)

alertcondition(resistanceTouched, "Price touched resistance level")
alertcondition(resistanceBroken, "Price closed above resistance level")

Implementing breakout strategies based on previous high

A simple breakout strategy enters a long position when the price closes above the previous highest high level. Stops can be placed below the breakout bar’s low or the resistance level itself.

pine
//@version=5
strategy("Previous High Breakout", overlay=true)

lookback = input.int(50, "Breakout Lookback", minval=1)
profitTargetPerc = input.float(2.0, "Profit Target (%) ") / 100
stopLossPerc = input.float(1.0, "Stop Loss (%) ") / 100

// Calculate the previous highest high
breakoutLevel = highest(high, lookback)[1]

// Define entry condition: current close breaks above the level calculated on the previous bar
// Ensure there's a valid level before trading
longCondition = close > breakoutLevel[0] and not na(breakoutLevel[0])

// Execute strategy orders
if longCondition
    strategy.entry("Long", strategy.long)

// Define exit conditions (take profit / stop loss)
strategy.exit("Exit", from_entry="Long", profit=strategy.position_avg_price * profitTargetPerc / syminfo.mintick, stop=strategy.position_avg_price * (1 - stopLossPerc) / syminfo.mintick)

// Optionally plot the breakout level
plot(breakoutLevel, "Breakout Level", color=color.green, linewidth=2, style=plot.style_line)

Remember to backtest and optimize parameters (lookback, profitTargetPerc, stopLossPerc) for the specific instrument and timeframe you are trading.

Integrating previous high in automated trading systems

In a complex automated system, the previous high can be one of many factors. It could be used:

  • As a filter: Only take long trades if the price is above the previous N-bar high.
  • As a target: Exit long positions when price reaches the previous N-bar high.
  • As a stop-loss adjustment: Trail a stop loss based on previous lows relative to the previous high.

Its role depends entirely on the overall strategy logic. The key is to calculate the level accurately using the methods discussed (highest(high, lookback)[1]) and then incorporate the resulting value into your decision-making logic (if, strategy.entry, strategy.exit, etc.). Always validate that the level is not na before using it in calculations or trade conditions.

By understanding how to reliably obtain the previous highest high and its variations, you unlock a fundamental building block for many technical analysis tools and automated trading strategies in Pine Script.


Leave a Reply