ZeroLagLSMA指标实现异常求助:PineScript转Python后运行崩溃
ZeroLagLSMA指标Backtrader重写崩溃问题排查与修复
问题背景
已完成ZeroLagLSMA(零滞后最小二乘移动平均线)的PineScript实现,代码如下:
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/ // © veryfid //@version=4 study(title = "ZLSMA - Zero Lag LSMA", shorttitle="ZLSMA", overlay=true, resolution="") length = input(title="Length", type=input.integer, defval=32) offset = input(title="Offset", type=input.integer, defval=0) src = input(close, title="Source") lsma = linreg(src, length, offset) lsma2 = linreg(lsma, length, offset) eq= lsma-lsma2 zlsma = lsma+eq plot(zlsma, color=color.yellow, linewidth=3)
尝试用Python Backtrader框架重写该指标时,创建第一个基于收盘价的LinReg实例看似正常,但创建第二个基于第一个LinReg输出的实例时,第一个lsma实例为空,导致程序崩溃。问题代码如下:
class BarIndex_ind(bt.Indicator): lines = ('index', ) def __init__(self): pass def next(self): self.lines.index[0] = len(self.data)+1 class LinReg(bt.Indicator): lines = ('line',) params = ( ('length', 32), ('source', None), ('offset', 0), #not using now ) def __init__(self): #self.addminperiod(self.p.length) self.barindex = BarIndex_ind() self.x_ = bt.ind.SMA(self.barindex, period=self.p.length) self.y_ = bt.ind.SMA(self.p.source, period=self.p.length) self.mx = bt.ind.StdDev(self.barindex, period=self.p.length) self.my = bt.ind.StdDev(self.p.source, period=self.p.length) def next(self): c = correlate(self.barindex.get(size=self.p.length), self.p.source.get(size=self.p.length), self.p.length) slope = c[1] * (self.my[0]/self.mx[0]) inter = self.y_[0] - slope * self.x_[0] linreg = self.barindex[0] * slope + inter self.l.line[0] = linreg class ZLSMA(bt.Indicator): lines = ('line',) params = ( ('length', 32), ('offset', 0), ) def __init__(self): #self.addminperiod(2*self.p.length) self.lsma = LinReg(source=self.data.close, length=self.p.length) self.lsma2 = LinReg(source=self.lsma.line, length=self.p.length) def next(self): eq = self.lsma[0] - self.lsma2[0] self.l.line[0] = self.lsma[0] + eq
问题原因
- 自定义BarIndex冗余且无周期控制:Backtrader内置了
bt.ind.BarIndex指标,无需手动实现;自定义版本未设置最小周期,导致数据生成时机错误。 - 最小周期未配置:LinReg和ZLSMA都注释掉了
addminperiod,导致指标在数据不足时提前尝试计算,访问空数据。 - 数据获取方式错误:
next中直接用get(size=self.p.length)获取数据,当数据源是另一个指标时,该指标的输出可能还未生成足够长度的历史数据,导致获取空值。 - 相关系数计算不规范:
correlate函数未明确来源,且未处理数据维度,容易引发计算错误。
修复后的代码
import numpy as np import backtrader as bt class LinReg(bt.Indicator): lines = ('line',) params = ( ('length', 32), ('source', None), ('offset', 0), ) def __init__(self): # 设置最小周期,确保有足够数据计算 self.addminperiod(self.p.length) # 使用Backtrader内置的BarIndex self.bar_index = bt.ind.BarIndex() # 预计算所需的统计量 self.x_sma = bt.ind.SMA(self.bar_index, period=self.p.length) self.y_sma = bt.ind.SMA(self.p.source, period=self.p.length) self.x_std = bt.ind.StdDev(self.bar_index, period=self.p.length) self.y_std = bt.ind.StdDev(self.p.source, period=self.p.length) def next(self): # 获取最近length条的bar索引和源数据 x_data = np.array(self.bar_index.get(size=self.p.length)) y_data = np.array(self.p.source.get(size=self.p.length)) # 计算皮尔逊相关系数 corr_matrix = np.corrcoef(x_data, y_data) corr = corr_matrix[0, 1] # 计算斜率和截距,避免除以零错误 slope = corr * (self.y_std[0] / self.x_std[0]) if self.x_std[0] != 0 else 0 intercept = self.y_sma[0] - slope * self.x_sma[0] # 计算当前LinReg值 self.lines.line[0] = self.bar_index[0] * slope + intercept class ZLSMA(bt.Indicator): lines = ('line',) params = ( ('length', 32), ('offset', 0), ) def __init__(self): # 设置ZLSMA的最小周期为2倍length,确保两次LinReg计算都有足够数据 self.addminperiod(2 * self.p.length) # 第一次LinReg基于收盘价 self.lsma = LinReg(source=self.data.close, length=self.p.length) # 第二次LinReg基于第一次的输出 self.lsma2 = LinReg(source=self.lsma.line, length=self.p.length) def next(self): eq = self.lsma[0] - self.lsma2[0] self.lines.line[0] = self.lsma[0] + eq
关键修复点
- 移除自定义
BarIndex_ind,改用Backtrader内置的bt.ind.BarIndex,确保数据生成符合框架规范。 - 为
LinReg添加self.addminperiod(self.p.length),为ZLSMA添加self.addminperiod(2 * self.p.length),保证计算时有足够的历史数据。 - 使用
numpy.corrcoef计算相关系数,确保计算逻辑稳定且符合统计规范。 - 增加除数为0的判断,避免运行时除以零错误。
内容的提问来源于stack exchange,提问作者Papay
相关产品推荐
相关产品推荐

