Search code examples
pine-scriptpine-script-v5

How to persist real-time buy/sell volume data in Pine Script after a set period?


I’m working on a Pine Script indicator that tracks the buy and sell volume during the first 20 seconds of a new 2-minute bar using 10-second data from a lower timeframe.

The issue I’m running into is that while I can calculate the volumes during that 20-second window, the values disappear or get overwritten by TradingView’s update cycles after they’re displayed.

What I want to achieve:

Live updates for the first 20 seconds of each 2-minute bar (to accumulate volume). After 20 seconds, I want to freeze the buy/sell volume values and persist them for the remaining 1 minute and 40 seconds of the bar. The table showing the volumes should stop updating after the first 20 seconds, but the values should remain visible until the next bar starts. I’ve tried using var and persistent variables, but I think I’m missing something with how Pine Script handles real-time data and updates.

Any ideas on how to achieve this without the values disappearing or being reset?

I’m using the following script, which gives a countdown timer to the start of the next 2-minute bar, and then displays the combined buy/sell volumes from the last 2x 10-second bars at the 20-second mark. However, the values disappear almost instantly after they are shown.

I've tried using var to store these values in persistent variables so that they would remain visible for the rest of the 2-minute bar, but I’m not able to get the values to persist as expected.

What I would ideally like to achieve:

Make the calculated values persist for the entire remaining duration of the 2-minute bar. Eventually, I’d like to extend this to show live tick-by-tick volume updates during the 20-second window.

Here’s the script I’m currently working with:

Thanks in advance!

//@version=5
indicator("Buy vs Sell Volumes (First 20s of 2-minute bar)", overlay=true)

// Inputs
main_timeframe = "2"  // 2-minute bars
lower_timeframe = "10S"  // 10-second bars

// Function to calculate buy and sell volumes
calc_volumes(vol, close, high, low) =>
    buy_vol = vol * (close - low) / (high - low)
    sell_vol = vol - buy_vol
    [buy_vol, sell_vol]

// Fetch lower timeframe data
[volume_10s, close_10s, high_10s, low_10s] = request.security_lower_tf(syminfo.tickerid, lower_timeframe, [volume, close, high, low])

// Permanent storage for volumes after 20 seconds
var float persistent_buy_vol = na
var float persistent_sell_vol = na
var string display_text = "Waiting for update..."

var int last_update_time = 0

// Update logic
update_volumes() =>
    float buy_sum = 0.0
    float sell_sum = 0.0
    if array.size(volume_10s) >= 2
        for i = 0 to 1
            vol = array.get(volume_10s, array.size(volume_10s) - 1 - i)
            close_val = array.get(close_10s, array.size(close_10s) - 1 - i)
            high_val = array.get(high_10s, array.size(high_10s) - 1 - i)
            low_val = array.get(low_10s, array.size(low_10s) - 1 - i)
            
            if high_val != low_val
                [buy_vol, sell_vol] = calc_volumes(vol, close_val, high_val, low_val)
                buy_sum += buy_vol
                sell_sum += sell_vol
    [buy_sum, sell_sum]

// Check if it's time to update (exactly 20 seconds into a new 2-minute bar)
current_time = time(main_timeframe)
seconds_into_bar = (timenow - current_time) / 1000
is_update_time = seconds_into_bar >= 20 and seconds_into_bar < 21 and current_time != last_update_time

if is_update_time
    [persistent_buy_vol, persistent_sell_vol] = update_volumes()
    display_text := "Last 20s - Buy: " + str.tostring(persistent_buy_vol, "#.##") + 
                     " | Sell: " + str.tostring(persistent_sell_vol, "#.##")
    last_update_time := current_time

// Ensure the display remains persistent for the whole 2-minute bar
if not na(persistent_buy_vol) and not na(persistent_sell_vol)
    display_text := "Buy: " + str.tostring(persistent_buy_vol, "#.##") + 
                     " | Sell: " + str.tostring(persistent_sell_vol, "#.##")

// Create the table once (ensure it's not recreated on every bar)
var table vol_table = table.new(position.bottom_right, 1, 2, frame_color=color.black, bgcolor=color.new(color.black, 70))

if barstate.islast
    table.cell(vol_table, 0, 0, "Volume Analysis", bgcolor=color.new(color.blue, 70), text_color=color.white)
    table.cell(vol_table, 0, 1, display_text, text_color=color.white)

// Display countdown
next_update_time = 120 - (seconds_into_bar % 120)
countdown_text = "Next update in " + str.tostring(math.round(next_update_time)) + "s"
var label countdown_label = label.new(bar_index, high, countdown_text, color=color.new(color.black, 80), textcolor=color.white, style=label.style_label_down)

// Set label to update on new and last bar
if barstate.islast
    label.set_xy(countdown_label, bar_index, high)
    label.set_text(countdown_label, countdown_text)

Solution

  • If you want your value to retain its value during each tick update on the same bar, you need to use the varip keyword.

    varip (var intrabar persist) is the keyword used for the assignment and one-time initialization of a variable or a field of a user-defined type. It’s similar to the var keyword, but variables and fields declared with varip retain their values between executions of the script on the same bar.

    varip float persistent_buy_vol = na
    varip float persistent_sell_vol = na
    varip string display_text = "Waiting for update..."
    
    varip int last_update_time = 0