PineScript kNN机器学习指标运行异常及加载过慢求助
问题描述
我修改了TradingView上的VWMA-with-kNN-Machine-Learning-MFI-ADX指标代码,新增多层EMA后出现两个问题:
- 指标未达到预期效果
- 加载耗时过长(免费账户需20秒,付费账户需40秒)
附上修改后的PineScript v5代码:
//@version=5 indicator("ema ML",overlay = false) em1=ta.ema(ta.ema(hlc3,3),3) em2=ta.ema(ta.ema(hlc3,5),5) em3=ta.ema(ta.ema(hlc3,8),8) em4=ta.ema(ta.ema(hlc3,13),13) em5=ta.ema(ta.ema(hlc3,21),21) plot(em1,color = color.red,linewidth = 2) plot(em2,color = color.green,linewidth = 2) plot(em3,color = color.blue,linewidth = 2) plot(em4,color = color.aqua,linewidth = 2) plot(em5,color = color.fuchsia,linewidth = 2) arrsma=array.from(em1,em2,em3,em4,em5) avgem=array.avg(arrsma) plot(avgem,color=color.black,linewidth = 3) src=close>open?1 : close<open?-1:0 ///////// // kNN // ///////// SRC=avgem // Define storage arrays for: parameter 1, parameter 2, price, result (up = 1; down = -1) var knn1 = array.new_float(1, 0) var knn2 = array.new_float(1, 0) var knnp = array.new_float(1, 0) var knnr = array.new_float(1, 0) // Store the previous trade; buffer the current one until results are in _knnStore(p1, p2, src) => var prevp1 = 0.0 var prevp2 = 0.0 var prevsrc = 0.0 array.push(knn1, prevp1) array.push(knn2, prevp2) array.push(knnp, prevsrc) array.push(knnr, src >= prevsrc ? 1 : -1) prevp1 := p1 prevp2 := p2 prevsrc := src prevsrc // Get neighbours by getting k smallest distances from the distance array, and then getting all results with these distances _knnGet(arr1, arr2, k) => sarr = array.copy(arr1) array.sort(sarr) ss = array.slice(sarr, 0, math.min(k, array.size(sarr))) m = array.max(ss) out = array.new_float(0) for i = 0 to array.size(arr1) - 1 by 1 if array.get(arr1, i) <= m array.push(out, array.get(arr2, i)) out // Create a distance array from the two given parameters _knnDistance(p1, p2) => dist = array.new_float(0) n = array.size(knn1) - 1 for i = 0 to n by 1 d = math.sqrt(math.pow(p1 - array.get(knn1, i), 2) + math.pow(p2 - array.get(knn2, i), 2)) array.push(dist, d) dist // Make a prediction, finding k nearest neighbours _knn(p1, p2, k) => slice = _knnGet(_knnDistance(p1, p2), array.copy(knnr), k) knn = array.sum(slice) knn //////////// // Inputs // //////////// // When DIST is 0, KNN default was 23 KNN = input(title='kNN nearest neighbors (k)', defval=45) DIST = input(title='kNN minimum difference', defval=2) BACKGROUND = input(title='Draw background', defval=false) //////// // MA // //////// buysell(x,y,z)=> _knnStore(x, y, z) filter = _knn(x, y, KNN) buy= (( filter >= DIST))?1 : 0 sell= ((filter <= -DIST))?1:0 [buy,sell] [buy,sell]=buysell(em1,em2,src) [buy1,sell1]=buysell(em2,em3,src) buycum=math.avg(buy,buy1) sellcum=math.avg(sell,sell1) buysig=buycum>sellcum sellsig=sellcum>buycum plotshape(buysig,style = shape.circle,location = location.top,color = color.green) plotshape(sellsig,style = shape.circle,location = location.top,color = color.red)
解决方案
一、性能优化(解决加载慢问题)
1. 限制kNN数组存储长度
原代码中knn1、knn2等数组会无限存储历史数据,K线越多遍历计算量越大。设置最大存储条数,超出后移除最早数据,直接减少遍历次数:
MAX_KNN_HISTORY = input.int("Max kNN History Length", defval=2000, minval=100) // 在_knnStore函数中添加长度限制逻辑 if array.size(knn1) > MAX_KNN_HISTORY array.shift(knn1) array.shift(knn2) array.shift(knnr)
2. 避免重复调用buysell()
原代码调用两次buysell(),每次都会往kNN数组新增数据,导致数组膨胀速度翻倍,计算量直接加倍。应重构逻辑:要么仅维护一组kNN数组,要么为不同特征组合单独维护独立的存储数组。
3. 优化kNN计算逻辑
- 提前缓存数组长度和元素,避免循环中重复调用
array.get()和array.size() - 合并距离计算与近邻筛选逻辑,减少中间数组的创建与复制(原代码中
_knnDistance()和_knnGet()的数组操作可合并为一次遍历+排序)
二、效果调整(解决指标不达预期问题)
1. 明确kNN特征逻辑
原代码同时将em1&em2、em2&em3两组特征存入同一kNN数组,导致特征维度混乱,预测结果无明确逻辑。建议:
- 选择单一特征组合(比如
em1&avgem)进行kNN训练 - 若要使用多组特征,为每组特征单独维护独立的kNN存储数组和计算逻辑
2. 调整kNN参数
当前默认KNN值为45,对于EMA特征来说过大,建议先调低至10-20测试;DIST参数需匹配EMA特征的差值范围,原指标的DIST值不适用于新特征,可先设为1.0左右测试。
3. 简化信号生成逻辑
原代码中buycum=math.avg(buy,buy1)的平均方式会稀释有效信号,建议先单独测试单一组特征的信号效果,再考虑多特征信号的组合方式(比如取交集而非平均)。
优化后代码示例
//@version=5 indicator("ema ML Optimized", overlay = false) // 缓存hlc3,减少重复计算 hlc3_val = hlc3 // 计算多层EMA em1 = ta.ema(ta.ema(hlc3_val, 3), 3) em2 = ta.ema(ta.ema(hlc3_val, 5), 5) em3 = ta.ema(ta.ema(hlc3_val, 8), 8) em4 = ta.ema(ta.ema(hlc3_val, 13), 13) em5 = ta.ema(ta.ema(hlc3_val, 21), 21) // 绘制EMA线 plot(em1, color = color.red, linewidth = 2) plot(em2, color = color.green, linewidth = 2) plot(em3, color = color.blue, linewidth = 2) plot(em4, color = color.aqua, linewidth = 2) plot(em5, color = color.fuchsia, linewidth = 2) // 计算平均EMA arrsma = array.from(em1, em2, em3, em4, em5) avgem = array.avg(arrsma) plot(avgem, color = color.black, linewidth = 3) // 定义涨跌标签 src = close > open ? 1 : close < open ? -1 : 0 ///////// // kNN 优化版 // ///////// // 设置kNN最大存储条数 MAX_KNN_HISTORY = input.int("Max kNN History Length", defval=2000, minval=100) // 定义存储数组 var knn1 = array.new_float(1, 0) var knn2 = array.new_float(1, 0) var knnr = array.new_float(1, 0) // 存储历史数据,超出长度则移除最早条目 _knnStore(p1, p2, src) => var prevp1 = 0.0 var prevp2 = 0.0 var prevsrc = 0.0 // 存储前一次的特征和结果 array.push(knn1, prevp1) array.push(knn2, prevp2) array.push(knnr, src >= prevsrc ? 1 : -1) // 限制数组长度 if array.size(knn1) > MAX_KNN_HISTORY array.shift(knn1) array.shift(knn2) array.shift(knnr) prevp1 := p1 prevp2 := p2 prevsrc := src prevsrc // 计算k近邻结果,优化逻辑减少数组操作 _knnGet(k) => p1_current = em1 p2_current = em2 arr_len = array.size(knn1) if arr_len <= 1 return 0.0 // 创建临时数组存储(距离, 结果)对 temp_arr = array.new_tuple() for i = 0 to arr_len - 1 d = math.sqrt(math.pow(p1_current - array.get(knn1, i), 2) + math.pow(p2_current - array.get(knn2, i), 2)) array.push(temp_arr, tuple(d, array.get(knnr, i))) // 按距离升序排序 array.sort(temp_arr, (a, b) => float.from_tuple(a, 0) < float.from_tuple(b, 0)) // 取前k个结果求和 take_k = math.min(k, arr_len - 1) // 排除初始的0值 result_sum = 0.0 for i = 1 to take_k // 跳过第一个初始元素 result_sum += float.from_tuple(array.get(temp_arr, i), 1) result_sum //////////// // Inputs // //////////// KNN = input.int('kNN nearest neighbors (k)', defval=15, minval=1) DIST = input.float('kNN minimum difference', defval=1.0) //////// // 信号生成 // //////// // 仅调用一次存储函数 _knnStore(em1, em2, src) filter = _knnGet(KNN) // 生成买卖信号 buy = filter >= DIST ? 1 : 0 sell = filter <= -DIST ? 1 : 0 // 绘制信号(分别放在顶部和底部,更易区分) plotshape(buy == 1, style = shape.circle, location = location.top, color = color.green, size=size.small) plotshape(sell == 1, style = shape.circle, location = location.bottom, color = color.red, size=size.small)
内容的提问来源于stack exchange,提问作者Babu Ramasamy
相关产品推荐
相关产品推荐

