You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Pine Script报错:request.security参数无副作用,求多品种警报方案

解决Pine Script中request.security副作用错误与多品种警报实现

问题根源

原代码报错的核心原因:

  • request.security要求传入的表达式必须是纯函数(无状态修改、无图表对象操作等副作用),但原代码中checkForAlert()依赖全局变量,且全局变量的赋值逻辑包含line.new/line.set_x2等修改图表对象的操作,违反了纯函数规则。
  • 全局变量isAlertLow/isAlertHigh在循环中被多次覆盖,最终仅保留最后一次循环结果,导致警报条件判断逻辑错误。
  • 多品种监控与当前图表绘制逻辑耦合,跨品种调用时执行不必要的线条绘制,既低效又触发副作用报错。

解决方案

核心思路

  1. 分离纯计算逻辑与副作用操作:将fractal计算、警报条件判断封装为纯函数,仅在当前图表执行线条绘制等副作用操作。
  2. 重构警报判断逻辑:在纯函数内独立维护每个品种的fractal状态,直接返回警报信息,不依赖全局变量。
  3. 优化多品种调用:通过request.security调用纯函数获取目标品种的警报状态,再触发警报。

修改后的完整代码

//@version=5
indicator('QQQGrail', shorttitle='QQQGrail', overlay=true, max_bars_back=144)

// 输入参数
n = input.int(title='Periods', defval=2, minval=2)
fractalBars = input.string(title='3 or 5 Bar Fractal', defval='3', options=['3', '5'])
arrayLength = input.int(title='Max nr of historical fractals', defval=20, minval=1, maxval=25)
plotActiveLines = input(title='Plot active lines', defval=true)

// ------------------------------
// 纯函数:计算Fractal
// ------------------------------
calcFractals() =>
    dnFractal = false
    upFractal = false
    
    if fractalBars == '5'
        dnFractal := high[n - 2] < high[n] and high[n - 1] < high[n] and high[n + 1] < high[n] and high[n + 2] < high[n]
        upFractal := low[n - 2] > low[n] and low[n - 1] > low[n] and low[n + 1] > low[n] and low[n + 2] > low[n]
    else // 3-bar fractal
        dnFractal := high[n - 1] < high[n] and high[n + 1] < high[n]
        upFractal := low[n - 1] > low[n] and low[n + 1] > low[n]
    
    [dnFractal, upFractal]

// ------------------------------
// 纯函数:判断流动性扫盘警报条件
// ------------------------------
checkSweepAlert() =>
    [dnFractal, upFractal] = calcFractals()
    
    // 维护 fractal 数组(纯内存操作,无图表副作用)
    var upFractalArray = array.new_int(arrayLength)
    var dnFractalArray = array.new_int(arrayLength)
    
    if dnFractal
        array.push(dnFractalArray, bar_index[n])
        array.shift(dnFractalArray)
    if upFractal
        array.push(upFractalArray, bar_index[n])
        array.shift(upFractalArray)
    
    isAlertHigh = false
    isAlertLow = false
    
    // 检查上扫条件
    for i = 0 to array.size(dnFractalArray) - 1
        dnBarIndex = array.get(dnFractalArray, i)
        fractalHigh = high[bar_index - dnBarIndex]
        
        // 判断是否存在更高的 fractal 限制
        limitCount = 0
        for t = i to array.size(dnFractalArray) - 1
            nextFractalIndex = array.get(dnFractalArray, t)
            if fractalHigh < high[bar_index - nextFractalIndex]
                limitCount := bar_index - nextFractalIndex
                break
        
        // 检查是否触发上扫
        wickFound = false
        for j = bar_index - dnBarIndex - 1 to limitCount by 1
            if fractalHigh < high[j]
                if fractalHigh < close[j]
                    break
                else
                    wickFound := true
                    // 仅判断是否到最新bar,不修改线条
                    if bar_index - j == bar_index
                        isAlertHigh := true
        
        if isAlertHigh
            break
    
    // 检查下扫条件
    for i = 0 to array.size(upFractalArray) - 1
        upBarIndex = array.get(upFractalArray, i)
        fractalLow = low[bar_index - upBarIndex]
        
        // 判断是否存在更低的 fractal 限制
        limitCount = 0
        for t = i to array.size(upFractalArray) - 1
            nextFractalIndex = array.get(upFractalArray, t)
            if fractalLow > low[bar_index - nextFractalIndex]
                limitCount := bar_index - nextFractalIndex
                break
        
        // 检查是否触发下扫
        wickFound = false
        for j = bar_index - upBarIndex - 1 to limitCount by 1
            if fractalLow > low[j]
                if fractalLow > close[j]
                    break
                else
                    wickFound := true
                    // 仅判断是否到最新bar,不修改线条
                    if bar_index - j == bar_index
                        isAlertLow := true
        
        if isAlertLow
            break
    
    // 返回警报信息
    alertMsg = ""
    if isAlertHigh
        alertMsg := str.format("Sweep High: Liquidity grab for {0} (Price: {1})", syminfo.ticker, high)
    if isAlertLow
        alertMsg := str.format("Sweep Low: Liquidity grab for {0} (Price: {1})", syminfo.ticker, low)
    if isAlertHigh or isAlertLow
        alertMsg
    else
        ""

// ------------------------------
// 当前图表的绘制逻辑(仅在当前品种执行,无跨品种副作用)
// ------------------------------
[dnFractal, upFractal] = calcFractals()

var upFractalArray_Plot = array.new_int(arrayLength)
var dnFractalArray_Plot = array.new_int(arrayLength)

if dnFractal
    array.push(dnFractalArray_Plot, bar_index[n])
    array.shift(dnFractalArray_Plot)
if upFractal
    array.push(upFractalArray_Plot, bar_index[n])
    array.shift(upFractalArray_Plot)

if barstate.islast
    // 绘制上 fractal 线条
    for i = 0 to array.size(dnFractalArray_Plot) - 1 by 1
        dnBarIndex = array.get(dnFractalArray_Plot, i)
        l = line.new(color=color.red, x1=dnBarIndex, y1=high[bar_index - dnBarIndex], x2=bar_index, y2=high[bar_index - dnBarIndex])
        wickFound = false
        limitCount = 0
        
        for t = i to array.size(dnFractalArray_Plot) - 1 by 1
            nextFractalIndex = array.get(dnFractalArray_Plot, t)
            if high[bar_index - dnBarIndex] < high[bar_index - nextFractalIndex]
                limitCount := bar_index - nextFractalIndex
                break
        
        for j = bar_index - dnBarIndex - 1 to limitCount by 1
            if high[bar_index - dnBarIndex] < high[j]
                if high[bar_index - dnBarIndex] < close[j]
                    if not wickFound
                        line.delete(l)
                    break
                else
                    wickFound := true
                    line.set_x2(l, bar_index - j)
        
        if not plotActiveLines and line.get_x2(l) == bar_index
            line.set_color(l, color.new(color.red, 100))
    
    // 绘制下 fractal 线条
    for i = 0 to array.size(upFractalArray_Plot) - 1 by 1
        upBarIndex = array.get(upFractalArray_Plot, i)
        l = line.new(color=color.green, x1=upBarIndex, y1=low[bar_index - upBarIndex], x2=bar_index, y2=low[bar_index - upBarIndex])
        wickFound = false
        limitCount = 0
        
        for t = i to array.size(upFractalArray_Plot) - 1 by 1
            nextFractalIndex = array.get(upFractalArray_Plot, t)
            if low[bar_index - upBarIndex] > low[bar_index - nextFractalIndex]
                limitCount := bar_index - nextFractalIndex
                break
        
        for j = bar_index - upBarIndex - 1 to limitCount by 1
            if low[bar_index - upBarIndex] > low[j]
                if low[bar_index - upBarIndex] > close[j]
                    if not wickFound
                        line.delete(l)
                    break
                else
                    wickFound := true
                    line.set_x2(l, bar_index - j)
        
        if not plotActiveLines and line.get_x2(l) == bar_index
            line.set_color(l, color.new(color.green, 100))

// 绘制 fractal 形状
plotshape(dnFractal, style=shape.triangledown, location=location.abovebar, offset=-n, color=color.new(color.red, 25))
plotshape(upFractal, style=shape.triangleup, location=location.belowbar, offset=-n, color=color.new(color.green, 25))

// ------------------------------
// 多品种警报触发逻辑
// ------------------------------
fireAlert(ticker, freq = alert.freq_once_per_bar) =>
    msg = request.security(ticker, timeframe.period, checkSweepAlert())
    if str.length(msg) > 0
        alert(msg, freq)

// 调用监控品种
fireAlert("AUDCAD")
fireAlert("AUDCHF")
fireAlert("AUDJPY")
fireAlert("AUDNZD")
fireAlert("AUDUSD")
fireAlert("CADCHF")
fireAlert("CADJPY")
fireAlert("CHFJPY")
fireAlert("EURAUD")
fireAlert("EURCAD")
fireAlert("EURCHF")
fireAlert("EURGBP")
fireAlert("EURJPY")
fireAlert("EURNZD")
fireAlert("EURUSD")
fireAlert("GBPAUD")
fireAlert("GBPCAD")
fireAlert("GBPCHF")
fireAlert("GBPJPY")
fireAlert("GBPNZD")
fireAlert("GBPUSD")
fireAlert("NZDCAD")
fireAlert("NZDCHF")
fireAlert("NZDJPY")
fireAlert("NZDUSD")
fireAlert("USDCAD")
fireAlert("USDCHF")
fireAlert("USDJPY")
fireAlert("XAUUSD")
fireAlert("US30USD")
fireAlert("NAS100USD")
fireAlert("SPX500USD")
fireAlert("GER30")
fireAlert("UK100GBP")

关键修改说明

  • 纯函数隔离:calcFractals()和checkSweepAlert()仅做内存计算,不修改图表对象,完全符合request.security的无副作用要求。
  • 绘制逻辑拆分:当前图表的线条绘制单独处理,跨品种监控时不执行这些操作,避免不必要的副作用。
  • 警报逻辑修正:每个品种的fractal数组独立维护,避免全局变量覆盖问题,确保警报判断准确。
  • 多品种调用优化:仅当目标品种触发警报时才生成消息并触发警报,提升效率。

内容的提问来源于stack exchange,提问作者Cyril

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 10:17:00