如何在PineScript中实现按日历天数映射序列下标获取历史值?
Let's tackle your problem step by step—you need a reliable way to map calendar days to a valid integer index for accessing historical series values in Pine Script, fixing the flaws in your initial approaches.
Core Issues with Your Current Methods
- Method 1: The
intervalvariable only gives a numeric multiplier (e.g., 5 for 5m or 5D) without context about the time unit, making it impossible to calculate accurate bars per day across different periods. - Method 2: Calculating
bars_per_daycan result in non-integer values (invalid for sequence indices), and direct index access likeclose[X]throws an "out of depth" error when X exceeds the number of loaded bars on the chart.
Solution: Reliable Day-to-Bar Mapping with Safe Value Access
Here's a robust implementation that addresses both problems:
Step 1: Calculate Accurate Bars Per Day
Instead of relying on interval, use the actual time difference between bars to compute seconds per bar, then derive bars per day. We'll handle edge cases like the first bar (where time[1] is na) and convert the result to an integer.
Step 2: Use Safe Value Retrieval
Avoid direct index access with potentially invalid integers. Instead, use ta.valuewhen() to fetch the value at the target offset, which gracefully handles cases where the target index exceeds loaded bars.
Full Pine Script Code
//@version=5 indicator("Day-Indexed Series Access", overlay=true) // Input: Number of calendar days to look back days_to_look_back = input.int(title="Days to Reference", defval=30, minval=1) // Calculate seconds per day sec_per_day = 24 * 60 * 60 // Compute seconds per bar (handle first bar's na value) deltat = time - time[1] seconds_per_bar = ta.sma(ta.missing(deltat) ? na : deltat / 1000, 10) // Use SMA to smooth outliers seconds_per_bar := ta.max(seconds_per_bar, 60) // Guard against zero/negative values // Calculate bars per day (convert to integer) bars_per_day = math.round(sec_per_day / seconds_per_bar) // Total bars to look back (integer index) target_bars = bars_per_day * days_to_look_back // Safe way to get the value: use ta.valuewhen() // We create a condition that's true for every bar, then take the Nth previous true value historical_value = ta.valuewhen(true, close, target_bars) // Alternative: Safe direct index access (check if index is valid) valid_index = target_bars <= bar_index historical_value_safe = valid_index ? close[target_bars] : na // Plot results plot(historical_value, color=color.red, title="N-Day Close (ValueWhen)") plot(historical_value_safe, color=color.blue, title="N-Day Close (Safe Index)", style=plot.style_circles)
Key Explanations
Seconds Per Bar Calculation:
- We use
ta.sma()to smooth out any outliers in bar time differences (e.g., gaps from market closures). ta.max()ensures we don't get invalid zero/negative values (which can happen on the first few bars).
- We use
Integer Conversion:
math.round()converts the calculatedbars_per_dayto an integer—you can also usemath.ceil()if you want to round up (to ensure you don't undershoot the target day) ormath.floor()to round down.
Safe Value Access:
ta.valuewhen(true, close, target_bars): This function fetches thetarget_bars-th occurrence oftrue(which is every bar), effectively giving us theclosevalue fromtarget_barsbars ago. It returnsnaif the target index is out of bounds, avoiding the "out of depth" error.- The
valid_indexcheck for direct access ensures we only use the index if it's within the range of loaded bars (sincebar_indexstarts at 0 for the first loaded bar).
Additional Tips
- For markets with non-24/7 trading (e.g., stock markets), adjust
sec_per_dayto the actual trading hours (e.g., 6.5 hours = 23400 seconds) to get more accurate bars per trading day. - If you need exact calendar days (including weekends/holidays), compare
timevalues directly: calculate the timestamp ofdays_to_look_backdays ago, then find the closest bar withta.valuewhen(time <= target_timestamp, close, 0).
内容的提问来源于stack exchange,提问作者JohnathanDough

