基于5分钟K线手动计算15分钟ATR偏移问题排查
自定义15分钟ATR与原生ATR偏移问题排查与修复
问题描述
我尝试通过修改版TR(真实波幅)和RMA函数,基于security函数获取的数据计算15分钟K线的ATR,开发已接近完成,但发现自定义计算的ATR与原生ATR存在偏移,无法定位问题原因,特附上Pine Script代码寻求帮助。
原代码
//@version=5 indicator("Table with Indicators", overlay=true, dynamic_requests=true) if timeframe.period != "5" log.error("This script runs on 5m timeframe only.") label.new(bar_index, high, "Please use 5m timeframe only!", color=color.red, textcolor=color.white, size=size.small) runtime.error("This script runs on 5m timeframe only.") rma(x, y, preSumIndex) => alpha = y var float sum = na sum := (x + (alpha - 1) * nz(sum[preSumIndex])) / alpha tr(_high, _low, preClose) => math.max(_high - _low, math.max(math.abs(_high - preClose), math.abs(_low - preClose))) var float atr15 = na var float trueRange = na var fixChartTimeframe = timeframe.period var timeframe = 15 period_length = timeframe/str.tonumber(fixChartTimeframe) timeframe_in_ms = timeframe * 60 * 1000 length = 14 current_time = time start_time = current_time - (current_time % timeframe_in_ms) time_elapsed = (current_time - start_time) / 1000 bars_since_period_start = int(time_elapsed / (5 * 60)) // 5 Minuten = 300 Sekunden int period_offset = math.round(bars_since_period_start % period_length) pre_candle_index = period_offset + 1 [h5, l5, c5] = request.security(syminfo.tickerid, "5", [high, low, close], lookahead=barmerge.lookahead_off) log.error(str.tostring(period_offset)) float h15 = h5[0] float l15 = l5[0] float c15 = c5[0] bool isCandleEnd = (period_offset == period_length - 1) or barstate.islast if period_offset > 0 h15 := ta.highest(h15, period_offset) l15 := ta.lowest(l15, period_offset) if isCandleEnd trueRange := tr(h5, l5, c5[pre_candle_index]) atr15 := rma(trueRange, length, pre_candle_index) atr15_native = request.security(syminfo.tickerid, str.tostring(timeframe), ta.atr(length), lookahead=barmerge.lookahead_off) plot(atr15_native, title="ATR 15min native", color=color.red, linewidth=2) plot(atr15, title="ATR 15min", color=color.blue, linewidth=2)
问题根源
- RMA函数逻辑错误:原生RMA的alpha是
1/length,而非直接传入length;递归依赖的是前一根目标周期的RMA值,而非自定义的偏移索引preSumIndex,这导致计算的平滑系数和递归逻辑完全错误。 - TR计算错误:自定义TR使用的是当前5分钟K线的高低价,而非合并后的15分钟K线高低价;且前收盘价用的是偏移后的5分钟收盘价,不是前一根15分钟K线的收盘价,导致真实波幅计算完全偏离标准。
- 周期更新时机错误:
isCandleEnd的判断逻辑存在误差,尤其是barstate.islast的加入会导致未闭合的15分钟K线也触发ATR更新,与原生ATR仅在K线闭合时更新的规则不符。
修复后的代码
//@version=5 indicator("Custom 15min ATR vs Native", overlay=false, dynamic_requests=true) // 仅允许在5分钟周期运行 if timeframe.period != "5" label.new(bar_index, high, "请使用5分钟周期!", color=color.red, textcolor=color.white, size=size.small) runtime.error("此脚本仅支持5分钟周期") // 标准RMA实现:alpha = 1/length,递归基于前一根值 rma_custom(x, length) => alpha = 1 / length var float rma_val = na rma_val := na(rma_val) ? ta.sma(x, length) : alpha * x + (1 - alpha) * nz(rma_val[1]) // 标准TR计算 tr_custom(_high, _low, _pre_close) => math.max(_high - _low, math.max(math.abs(_high - _pre_close), math.abs(_low - _pre_close))) // 目标周期参数 target_tf = "15" atr_length = 14 // 获取15分钟周期的高低收数据(包含历史完整K线) [h15, l15, c15] = request.security(syminfo.tickerid, target_tf, [high, low, close], lookahead=barmerge.lookahead_off) // 获取前一根15分钟K线的收盘价 pre_c15 = request.security(syminfo.tickerid, target_tf, close[1], lookahead=barmerge.lookahead_off) // 仅当15分钟K线闭合时计算TR和更新ATR var float custom_atr = na if barstate.isrealtime ? ta.change(time(timeframe.period, target_tf)) : true current_tr = tr_custom(h15, l15, pre_c15) custom_atr := rma_custom(current_tr, atr_length) // 获取原生15分钟ATR native_atr = request.security(syminfo.tickerid, target_tf, ta.atr(atr_length), lookahead=barmerge.lookahead_off) // 绘图对比 plot(native_atr, title="原生15分钟ATR", color=color.red, linewidth=2) plot(custom_atr, title="自定义15分钟ATR", color=color.blue, linewidth=2)
关键修复点
- 实现标准RMA算法:初始值用SMA,递归使用
alpha * 当前值 + (1-alpha)*前值的标准逻辑 - 正确计算15分钟TR:使用合并后的15分钟K线高低价,以及前一根15分钟K线的收盘价
- 精准控制ATR更新时机:仅在15分钟K线闭合时(通过
ta.change(time(...))判断周期切换)更新ATR,与原生ATR的更新逻辑完全同步 - 简化数据获取:直接通过
request.security获取完整的15分钟K线数据,避免手动合并5分钟数据的误差
内容的提问来源于stack exchange,提问作者Sailoshi
相关产品推荐
相关产品推荐

