基于Voss滤波器和趋势指标的量化交易策略


创建日期: 2023-09-19 16:59:10 最后修改: 2023-09-19 16:59:10
复制: 2 点击次数: 900
avatar of ChaoZhang ChaoZhang
1
关注
1617
关注者

概述

该策略综合利用Voss预测滤波器和Ehlers瞬时趋势线指标,以识别市场周期性转折点,实现量化交易。Voss滤波器可提前发出买入/卖出信号,而瞬时趋势线指标则用于判断整体趋势方向,减少Voss滤波器在趋势市场中的误导。该策略可用于比特币等周期性比较明显的品种,在回测中表现较好。

策略原理

Voss预测滤波器

Voss预测滤波器来自约翰·F·埃勒斯的文章《A Peek Into The Future》。该滤波器的计算公式如下:

_filt = 0.5 * _s3 * _x1 + _f1 * _s2 * _filt[1] - _s1 * _filt[2]
_voss = _x2 * _filt - _sumC

其中,_x1为价格的一阶差分;_x2为平滑因子;_s1、_s2、_s3为滤波参数;_f1为周期参数;_filt为滤波结果;_voss为最终输出。

该滤波器可看作是一种平滑滤波,它强调当前和过去几个周期的信息,从而提前发出买入/卖出信号。由于内在的组 delays,它就像“看进了未来”一样,可在其他指标之前发出预测性信号。

瞬时趋势线指标

瞬时趋势线指标由以下公式计算:

_it = (_a-((_a*_a)/4.0))*_src+0.5*_a*_a*_src[1]-(_a-0.75*_a*_a)*_src[2]+2*(1-_a)*nz(_it[1])+-(1-_a)*(1-_a)*nz(_it[2])

该指标实时绘制一条与价格最符合的趋势线,可准确判断趋势方向和强弱。

策略逻辑

当Voss由负转正,并上穿滤波结果时产生买入信号。

当Voss由正转负,并下穿滤波结果时产生卖出信号。

同时,只有当瞬时趋势线指标确认趋势方向时,才会发出交易信号。这能过滤掉Voss滤波器在趋势市场中可能发出的错误信号。

策略优势

  • Voss滤波器可提前发出预测性信号,捕捉周期性反转点
  • 瞬时趋势线指标可准确判断趋势方向,避免因滤波器提前发出的错误信号
  • 可配置参数进行优化,适用于不同周期和市场环境
  • 可添加止损策略控制风险

策略风险及解决方法

  • 该策略依赖滤波器提前发出信号,可能跳过部分趋势行情
  • 在强势趋势下,可能产生反作用交易信号,带来损失

可通过以下方法降低风险:

  • 优化周期参数,匹配不同品种周期性
  • 调整带宽参数,降低滤波强度,减少误导信号
  • 增加趋势过滤,避免强势趋势下的错误信号
  • 设置止损策略,控制单笔损失

策略优化方向

该策略可从以下方面进行优化:

  • 尝试不同的价格源,如收盘价、均线等,获得更好的输入
  • 调整滤波器周期参数,匹配具体品种的周期性
  • 优化趋势指标参数,获取更准确的趋势判断
  • 尝试不同的趋势指标,寻找更合适的组合
  • 添加止损、 trailing stop 策略,更好控制风险
  • 进行参数优化,寻找最佳参数组合

总结

该策略综合Voss滤波器和趋势指标,可有效识别市场的周期性反转点。通过优化参数,控制风险,该策略可实现稳定的量化交易系统。它可广泛应用于具有明显周期性的品种,在回测中已展现出良好的交易效果。总体而言,该策略具有独特的预测能力,且可通过多方面优化,具有广阔的应用前景。

策略源码
/*backtest
start: 2023-08-19 00:00:00
end: 2023-09-18 00:00:00
period: 1h
basePeriod: 15m
exchanges: [{"eid":"Futures_Binance","currency":"BTC_USDT"}]
*/

// A Peek Into the Future
// John F. Ehlers
// TASC Aug 2019

// Created by e2e4mfck for tradingview.com
// Modified by © Bitduke

//@version=4
//strategy("Voss Strategy (Filter + IT)", overlay=false, calc_on_every_tick=false,pyramiding=0, default_qty_type=strategy.cash,default_qty_value=1000, currency=currency.USD, initial_capital=1000,commission_type=strategy.commission.percent, commission_value=0.075)

// voss filter

source = input(close, type = input.source)
period = input(20, type = input.integer)
predict = input(4, type = input.integer)
bandwidth = input(0.25, type = input.float)

// it trendline

src = input(hl2, title="Source IT")
a = input(0.07, title="Alpha", step=0.01) 
fr = input(false, title="Fill Trend Region")
ebc = input(false, title="Enable barcolors")
hr = input(false, title="Hide Ribbon")


voss_filter (_period, _predict, _bandwidth, _source) =>
	float _filt = 0, float _sumC = 0, float _voss = 0
	_PI		= 2 * asin(1)
	_order	= 3 * _predict
	_f1		= cos(2 * _PI / _period)
	_g1		= cos(_bandwidth * 2 * _PI / _period)
	_s1		= 1 / _g1 - sqrt(1 / (_g1 * _g1) - 1)
	_s2		= 1 + _s1
	_s3		= 1 - _s1
	_x1		= _source - _source[2]
	_x2		= (3 + _order) / 2

	for _i = 0 to (_order - 1)
		_sumC := _sumC + ((_i + 1) / _order) * _voss[_order - _i]

	if bar_index <= _order
		_filt := 0		
		_voss := 0		
	else			
		_filt := 0.5 * _s3 * _x1 + _f1 * _s2 * _filt[1] - _s1 * _filt[2]
		_voss := _x2 * _filt - _sumC

	[_voss, _filt]


[Voss, Filt] = voss_filter(period, predict, bandwidth, source)


instantaneous_trendline (_src, _a, _freq, _ebc, _hr) =>
    _it = 0.0
    _it := (_a-((_a*_a)/4.0))*_src+0.5*_a*_a*_src[1]-(_a-0.75*_a*_a)*_src[2]+2*(1-_a )*nz(_it[1], ((_src+2*_src[1]+_src[2])/4.0))-(1-_a)*(1-_a)*nz(_it[2], ((_src+2*_src[1]+_src[2])/4.0))
    _lag = 2.0*_it-nz(_it[2])
    
    [_it, _lag]

[it, lag] = instantaneous_trendline(src, a, fr, ebc, hr)

// - - - - -  - - - - - //

plot(Filt, title = "Filter", style = plot.style_line, color = color.red, linewidth = 2)
plot(Voss, title = "Voss", style = plot.style_line, color = color.blue,	linewidth = 2)
hline(0.0, title = "Zero", linestyle = hline.style_dashed, color = color.black,	linewidth = 1)
plot(hr? na:it, title="IT Trend", color= fr? color.gray : color.red, linewidth=1)
plot(hr? na:lag, title="IT Trigger", color=fr? color.gray : color.blue, linewidth=1)


// Strategy Logic
longCondition =  lag < it  and crossover(Voss,Filt) 
shortCondition = it > lag and crossover(Filt,Voss) 

strategy.entry("Voss_Short", strategy.short, when=shortCondition)
strategy.entry("Voss_Long", strategy.long, when=longCondition)


// === Backtesting Dates === thanks to Trost

testPeriodSwitch = input(true, "Custom Backtesting Dates")
testStartYear = input(2019, "Backtest Start Year")
testStartMonth = input(1, "Backtest Start Month")
testStartDay = input(1, "Backtest Start Day")
testStartHour = input(0, "Backtest Start Hour")
testPeriodStart = timestamp(testStartYear, testStartMonth, testStartDay, testStartHour, 0)
testStopYear = input(2020, "Backtest Stop Year")
testStopMonth = input(2, "Backtest Stop Month")
testStopDay = input(29, "Backtest Stop Day")
testStopHour = input(0, "Backtest Stop Hour")
testPeriodStop = timestamp(testStopYear, testStopMonth, testStopDay, testStopHour, 0)
testPeriod() =>
    time >= testPeriodStart and time <= testPeriodStop ? true : false
testPeriod_1 = testPeriod()
isPeriod = true
// === /END

if not isPeriod
    strategy.cancel_all()
    strategy.close_all()