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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 02:42:01