在TradingView中实现卡尔曼滤波算法

简介

卡尔曼滤波(Kalman Filter)是一种递归算法,广泛应用于信号处理、控制系统和动态系统的估计。它能够根据系统的历史状态和当前观测值对当前状态进行预测和修正,尤其在噪声较大的情况下表现优秀。在金融领域,卡尔曼滤波可以用于平滑市场数据,提取隐藏的趋势,去除价格波动中的噪音。

卡尔曼滤波算法概述

卡尔曼滤波是一种递归的最优估计方法,常用于处理带噪声的线性动态系统。其核心思想是结合系统的预测信息与观测信息,通过加权平均的方式来不断更新状态估计值。卡尔曼滤波主要包括两个步骤:

  1. 预测:根据上一时刻的估计值和系统模型预测当前时刻的状态。
  2. 更新:通过当前的观测值对预测结果进行修正。
  • 基本步骤
    • 预测阶段:根据历史数据和估计模型预测当前的状态。
    • 更新阶段:利用当前的观测数据(例如市场价格)来更新状态估计。
    • 加权平均:预测值和观测值的加权平均决定了最终的状态估计,其中权重由卡尔曼增益计算得出。

指标的主要目标和效果

1. 提取市场的真实趋势

在金融市场中,价格数据经常受到噪音和随机波动的影响,这些短期的价格变化往往让交易者误判市场的真实趋势。卡尔曼滤波的主要目的是通过对历史数据的递归估计和修正,去除这些短期波动的干扰,帮助交易者识别价格的长期趋势。卡尔曼滤波不仅考虑了历史状态,还结合了当前的观测值,因此能够适应市场的变化,提供平滑且及时的趋势预测。

2. 降低噪音的影响

市场数据中存在的噪音可能来自多个方面,如市场的突发事件、短期的市场情绪波动或交易策略的波动等。卡尔曼滤波通过状态估计与修正,可以有效降低这些噪音的影响,提取更稳定的趋势信号。这样,卡尔曼滤波能够使得交易者更好地识别价格背后的长期变化,而不是过度反应市场中的随机波动。

3. 自适应性强

卡尔曼滤波算法具有自适应特性,可以根据实际市场的波动动态调整滤波的强度。在代码中,滤波的过程噪声和观测噪声协方差矩阵是根据实际的市场数据(如交易量)进行调整的。这种自适应机制使得卡尔曼滤波能够在不同的市场环境下保持较高的准确性,例如在高波动性市场中,卡尔曼滤波能够更加灵敏地响应市场的变化,而在低波动性市场中,它则能平滑价格波动。

4. 平滑市场数据,减少过度反应

通过卡尔曼滤波,交易者可以看到平滑后的趋势线,而不被市场的短期波动所干扰。在许多交易策略中,平滑的数据有助于消除误导性的买入或卖出信号,避免因过度反应短期的价格波动而导致错误的决策。

5. 提供支撑与阻力带

在代码中,还使用了卡尔曼滤波的估计值来计算价格的支撑带和阻力带。通过这些带状区间,交易者可以更好地判断市场价格的可能波动范围,进而做出更有根据的交易决策。这些带状区间不仅反映了价格的潜在波动范围,还考虑了市场的波动性,因此能够帮助交易者识别可能的反转点。

具体效果如下:

实现方法


// This Pine Script™ code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © openbox6
//@version=5
indicator("卡尔曼滤波 - 老庄不是庄 - YouTube", overlay = true)
xv = 1 // 标准差宽度默认值
N1 = 5 // 计算强度比例
src = close
// 定义状态向量,初始值分别为0.0, 0.0
var X = array.from(0.0, 0.0)
// 定义一个由浮点数组成的空数组
var osc_buffer = array.new<float>()
var Y_diff = array.new<float>()
// 定义时序窗口长度(回看多少根数据计算)
N_1 = 20
// 是否启用高波动率来计算支持带(如果市场波动较大,可视情况开启该功能)
volla = false

// 定义噪音
pNoise1  = 0.01 
pNoise2  = 0.01
measNoise1 = input.float(500, '信噪', step=50, minval = 0, maxval = 1000, group = '交易设置')



// 初始化状态转移矩阵 2X2
var F = matrix.new<float>(2, 2, 0.0)
F.set(0, 0, 1.0)
F.set(0, 1, 1.0)
F.set(1, 0, 1.0)

// 初始化误差协方差矩阵 2X2
var P = matrix.new<float>(2, 2, 0.0)
matrix.set(P, 0, 0, 1.0)
matrix.set(P, 1, 1, 1.0)

// 过程噪声协方差矩阵 2X2
var Q = matrix.new<float>(2, 2, 0.0)
matrix.set(Q, 0, 0, pNoise1)
matrix.set(Q, 0, 1, pNoise1*pNoise2)
matrix.set(Q, 1, 0, pNoise2*pNoise1)
matrix.set(Q, 1, 1, pNoise2)

// 观测矩阵,用于从状态空间映射到观测空间 1X2
var H = matrix.new<float>(1, 2, 0.0)
matrix.set(H, 0, 0, 1.0)

//  观测噪声协方差矩阵
var R = matrix.new<float>(1, 1, measNoise1)

// 定义单位矩阵 2X2
var I = matrix.new<float>(2, 2, 0.0)
matrix.set(I, 0, 0, 1.0)
matrix.set(I, 1, 1, 1.0)

// 卡尔曼滤波算法
// param X 当前状态向量,代表趋势
// param P 当前误差协方差矩阵,用于描述状态的不确定性
// param F 状态转移矩阵,描述状态从当前时间步到下一个时间步的变化
// param Q 过程噪声协方差矩阵,描述预测过程中引入的不确定性
// param H 观测矩阵,用于从状态空间映射到观测空间
// param R 观测噪声协方差矩阵,描述测量误差的不确定性
// param I 单位矩阵,用于协方差更新计算
// param src 当前观测值,例如收盘价
// param Y_diff 误差缓冲区,存储观测值与预测值的差异
kalmanStep(X, P, F, Q, H, R, I, src, Y_diff) =>
    // 预测下一步
    x1 = matrix.get(F, 0, 0) * array.get(X, 0) + matrix.get(F, 0, 1) * array.get(X, 1)
    x2 = matrix.get(F, 1, 1) * array.get(X, 1)
    pred_X = array.from(x1, x2)
    pred_P = F.mult(P.mult(F.transpose())).sum(Q)
    array.push(Y_diff, src - array.get(pred_X, 0))

    // 调整观测噪声
    R_adjusted = R.copy()
    if bar_index > 2
        matrix.set(R_adjusted, 0, 0, matrix.get(R, 0, 0) * volume[1] / math.min(volume[1], volume))

    // 更新状态
    S = H.mult(pred_P.mult(H.transpose())).sum(R_adjusted)
    K = pred_P.mult(H.transpose().mult(S.inv()))
    innovation = src - array.get(H.mult(pred_X), 0)
    diff = K.mult(innovation)
    new_X = array.from(array.get(pred_X, 0) + matrix.get(diff, 0, 0), array.get(pred_X, 1) + matrix.get(diff, 1, 0))
    new_P = I.sum(K.mult(H).mult(-1)).mult(pred_P)

    [pred_X, pred_P, new_X, new_P]


// 计算估计值、振荡器、滤波后的数据源以及上下波动带
calcEst(X, osc_buffer, Y_diff, N, volla) =>
    estimate = array.get(X, 0)
    oscillator = array.get(X, 1)
    filtered_src = estimate
    array.push(osc_buffer, oscillator)

    // Calculate bands if Y_diff has enough data
    float lower_band = na
    float upper_band = na
    smstd = ta.ema(ta.stdev(close, xv),5)
    if array.size(Y_diff) >= N
        variance = array.stdev(Y_diff)
        lower_band := volla ? (estimate - variance)-smstd : (ta.wma(filtered_src, N) - variance)
        upper_band := volla ? (estimate + variance)+smstd : (ta.wma(filtered_src, N) + variance)
        array.shift(Y_diff)

    [estimate, oscillator, filtered_src, lower_band, upper_band]

[_, _, new_X, new_P] = kalmanStep(X, P, F, Q, H, R, I, src, Y_diff)
X := new_X  // 更新状态向量
P := new_P  // 更新协方差矩阵

// 使用自定义函数计算卡尔曼滤波的趋势线和支持阻力
[estimate, oscillator, filtered_src, lower_band, upper_band]      = calcEst(X, osc_buffer, Y_diff, N_1, volla)

// 平滑处理
kalman_trend = ta.sma(filtered_src, N_1)

trend_color = kalman_trend > kalman_trend[1] ? color.rgb(175, 255, 105, 0) : color.rgb(255, 71, 80, 0)

// 绘制卡尔曼滤波趋势线
av_plot = plot(kalman_trend, color=trend_color, title="Adaptive Kalman Filter", linewidth = 2, style=plot.style_linebr)
up_plot = plot(upper_band, color=na, title="Upper band")
lo_plot = plot(lower_band, color=na, title="Lower band")
fill(up_plot, av_plot, upper_band, kalman_trend, #91d5ff4d, na)
fill(lo_plot, av_plot, lower_band, kalman_trend, #91d5ff4d, na)

总结

这段代码的目的是通过卡尔曼滤波对市场价格进行平滑处理,去除噪音,提供一个更准确的趋势线,帮助交易者更好地识别市场中的真实趋势。通过卡尔曼滤波,交易者可以在市场中获得更清晰的信号,减少因短期波动带来的误导,并为制定更稳定的交易策略提供支持。

发表回复 0

Index