自适应网格交易策略是一种基于网格交易系统的量化策略,它通过自动调整网格线位置来适应市场变化。该策略使用多种技术指标计算最佳交易点位,并根据价格变动动态更新网格。核心思想是在设定的价格区间内,当价格触及预设网格线时执行买入或卖出操作,从而捕捉市场波动带来的获利机会。策略特色在于其弹性机制(Elasticity)和延迟参数(Laziness),使网格能够自动调整以适应不同市场环境,实现更加灵活的交易执行。
该策略基于以下核心组件和工作原理:
平滑处理机制: 策略首先对价格进行平滑处理,支持多种移动平均类型(线性回归、SMA、EMA、VWMA和TEMA),用户可根据偏好选择合适的平滑方法。
延迟参数(Laziness): 这是策略的关键创新点,通过引入延迟函数lz(),只有当价格变动超过一定百分比时,系统才会更新信号,有效过滤市场噪音。
网格构建机制:
信号生成逻辑:
交易控制机制:
动态网格更新: 当Lazy Moving Average(LMA)变化时,整个网格结构会重新调整,使策略能够适应新的价格区间。
策略使用数组存储各个网格线价格,通过计算价格与网格线的交叉来确定具体的买卖点位,同时考虑了多种约束条件以避免不必要的交易。
自适应性强: 该策略最大的优势在于能够根据市场变化自动调整网格位置,无需人工干预。通过弹性参数和锚点调整机制,网格可以随着价格趋势的改变而移动,始终保持相关性。
噪音过滤: 延迟参数(Laziness)的引入是一项创新,它确保只有当价格变化足够显著时才触发网格调整,有效减少了对市场噪音的反应,提高了策略的稳定性。
灵活定制: 策略提供了丰富的参数设置,包括网格数量、网格间隔、方向偏好、平滑类型等,使用者可以根据不同市场特性和个人交易风格进行调整。
可视化交易区域: 策略将当前活跃的交易区间通过颜色填充显示,使交易者能够直观地了解当前价格在网格中的位置,便于决策。
风险控制: 通过限制交易只能在特定网格范围内进行,策略建立了自然的风险控制机制,防止在极端市场条件下的不利交易。
统一的进出场逻辑: 使用相同的网格线作为买入和卖出信号,保持了交易逻辑的一致性和可预测性。
区间突破风险: 该策略本质上是区间交易策略,在强趋势市场中可能面临持续性亏损。当价格突破网格上下限并持续单向移动时,策略可能会在错误的方向上持续加仓。解决方法是增加趋势识别组件,或在确认趋势成立时暂停网格交易。
参数敏感性: 策略性能高度依赖于参数设置,尤其是延迟参数(Laziness)和弹性参数(Elasticity)。不适当的参数可能导致网格调整不及时或过度敏感。建议通过回测在不同市场环境下优化这些参数。
金字塔式持仓风险: 策略允许在同一方向上多次入场(pyramiding=4),在极端市场条件下可能导致过度杠杆和风险集中。应考虑设置最大持仓限制和动态仓位管理。
滑点和手续费影响: 网格交易策略通常涉及频繁交易,在实际执行中,滑点和手续费可能显著影响策略盈利能力。需要在回测中纳入这些因素,并可能需要调整网格间隔以平衡交易频率与成本。
信号冲突处理: 当买卖信号同时出现时,当前策略选择忽略两个信号,这可能导致错过重要的交易机会。可考虑基于额外的市场指标或价格模式来解决信号冲突。
自适应参数调整: 策略可以进一步优化为根据市场波动性自动调整网格间隔和延迟参数。例如,在高波动性市场中增加网格间隔,在低波动性市场中减小网格间隔,使策略能更好地适应不同市场条件。
整合趋势识别组件: 当前策略在趋势市场中表现可能不佳,可以引入趋势识别指标(如ADX、移动平均线交叉等),在识别到强趋势时自动调整交易方向或暂停网格交易。
动态仓位管理: 目前策略使用固定仓位大小,可以改进为基于风险计算的动态仓位管理,例如根据ATR(平均真实波幅)调整仓位大小,或根据账户净值百分比分配资金。
多时间框架分析: 引入多时间框架分析,使用更长时间周期的趋势方向来过滤交易信号,只在顺应更大时间框架趋势的方向上执行网格交易。
止损机制完善: 当前策略缺乏明确的止损机制,可以添加基于整体市场状况的全局止损,或为每个网格级别设置单独的止损点,以限制单笔交易的最大亏损。
进出场时机优化: 策略可以整合成交量或价格动量指标,在网格信号触发时,通过额外的过滤条件优化具体的进出场时机,提高成功率。
机器学习整合: 可以考虑使用机器学习算法优化网格位置和参数选择,通过历史数据训练模型预测最佳网格设置,进一步提高策略的适应性。
自适应网格交易策略通过创新的延迟函数和动态网格调整机制,解决了传统网格交易策略缺乏灵活性的问题。它能够自动适应市场变化,在不同价格区间内捕捉交易机会,同时通过多种参数控制交易行为。该策略适合在震荡市场中应用,可以通过设置合理的网格间隔和方向偏好,实现自动化的交易执行。
尽管存在区间突破风险和参数敏感性等潜在问题,但通过整合趋势识别和动态参数调整等优化方向,该策略有潜力在各种市场环境中取得稳定表现。在实际应用中,建议先通过全面回测验证策略性能,特别是在不同市场条件下的表现,并根据具体交易品种特性调整参数,以达到最佳效果。
//@version=5
// This source code is subject to the terms of the Mozilla Public License 2.0 https://mozilla.org/MPL/2.0/
// ©mvs1231 || xxattaxx
strategy(title='Grid Bot Auto Strategy', shorttitle='GridBot', initial_capital = 100000, overlay=true, pyramiding=4, default_qty_type = strategy.fixed, default_qty_value = 0, commission_value = 0.04, commission_type = strategy.commission.percent, margin_long = 0, margin_short = 0, process_orders_on_close = true)
//----<User Inputs>------------------------------------------------------------------------------//
iLen = input.int(7, 'Smoothing Length(7)', minval=1)
iMA = input.string('lreg', 'Smoothing Type', options=['lreg', 'sma', 'ema', 'vwma', 'tema'])
iLZ = input.float(4.0, 'Laziness(4%)', step=.25) / 100
iELSTX = input(50.0, 'Elasticity(50)')
iGI = input.float(2.0, 'Grid Interval(2%)', step=.25) / 100
iGrids = input.int(6, 'Number of Grids', options=[2, 4, 6, 8])
iCool = input.int(2, 'Cooldown(2)', minval=0)
iDir = input.string('neutral', 'Direction', options=['neutral', 'up', 'down'])
iGT = input.int(70, 'Grid Line Transparency(100 to hide)', minval=0, maxval=100)
iFT = input.int(90, 'Fill Transparency(100 to hide)', minval=0, maxval=100)
iSS = input.string('small', 'Signal Size', options=['small', 'large'])
iReset = input(true, 'Reset Buy/Sell Index When Grids Change')
iEXTR = input(true, 'Use Highs/Lows for Signals')
iMT = input(true, 'Show Min Tick')
iRFC = input(false, 'Reverse Fill Colors')
qty_ent = iGrids/2
qty_pos = strategy.initial_capital / qty_ent / 2 / open
//----<Colors>-----------------------------------------------------------------------------------//
RedGrid = color.new(color.red, iGT)
GreenGrid = color.new(color.green, iGT)
Crimson = #DC143C
LimeGreen = #32CD32
//----<Variables>--------------------------------------------------------------------------------//
NextUP = 0.0
NextDN = 0.0
LastSignal = 0
LastSignal_Index = 0
AP = 0.0
G = iGrids
Buy = false
Sell = false
UpperLimit = 0.0
LowerLimit = 0.0
SignalLine = 0.0
CurrentGrid = 0.0
BuyLine = 0.0
SellLine = 0.0
DIR = 0
MeaningOfLife = 42
//----<Calculations>-----------------------------------------------------------------------------//
//Lazy Formula
lz(x, lzf) =>
LZ = 0.0
s = math.sign(x)
LZ := x == nz(x[1], x) ? x : x > nz(LZ[1] + lzf * LZ[1] * s, x) ? x : x < nz(LZ[1] - lzf * LZ[1] * s, x) ? x : LZ[1]
LZ
//Smoothing
LR = ta.linreg(close, iLen, 0)
SMA = ta.sma(close, iLen)
EMA = ta.ema(close, iLen)
VWMA = ta.vwma(close, iLen)
TEMA = ta.ema(ta.ema(ta.ema(close, iLen), iLen), iLen)
MA = iMA == 'lreg' ? LR : iMA == 'sma' ? SMA : iMA == 'ema' ? EMA : iMA == 'vwma' ? VWMA : TEMA
//Make Lazy
LMA = lz(MA, iLZ)
//Calculate Elasticity
ELSTX = syminfo.mintick * iELSTX
//Show Mintick
if iMT and barstate.islast
table.cell(table.new(position.top_right, 1, 1), 0, 0, str.tostring(syminfo.mintick))
//Anchor Point
AP := MA > LMA ? AP[1] + ELSTX : MA < LMA ? AP[1] - ELSTX : AP[1]
AP := AP >= NextUP[1] ? NextUP[1] : AP
AP := AP <= NextDN[1] ? NextDN[1] : AP
//Reset if Next Level Reached or AP is crossed
AP := LMA != LMA[1] ? LMA : AP
//Next Gridlines
NextUP := LMA != LMA[1] ? LMA + LMA * iGI : NextUP[1]
NextDN := LMA != LMA[1] ? LMA - LMA * iGI : NextDN[1]
//Grid Interval
GI = AP * iGI
//----<Grid Array>-------------------------------------------------------------------------------//
a_grid = array.new_float(9)
for x = -4 to 4 by 1
array.set(a_grid, x + 4, AP + GI * x)
Get_Array_Values(ArrayName, index) =>
value = array.get(ArrayName, index)
value
//----<Set Static Grids>-------------------------------------------------------------------------//
G0 = Get_Array_Values(a_grid, 0) //Upper4
G1 = Get_Array_Values(a_grid, 1) //Upper3
G2 = Get_Array_Values(a_grid, 2) //Upper2
G3 = Get_Array_Values(a_grid, 3) //Upper1
G4 = Get_Array_Values(a_grid, 4) //Center
G5 = Get_Array_Values(a_grid, 5) //Lower1
G6 = Get_Array_Values(a_grid, 6) //Lower2
G7 = Get_Array_Values(a_grid, 7) //Lower3
G8 = Get_Array_Values(a_grid, 8) //Lower4
//----<Set Upper and Lower Limits>---------------------------------------------------------------//
UpperLimit := G >= 8 ? G8 : G >= 6 ? G7 : G >= 4 ? G6 : G5
LowerLimit := G >= 8 ? G0 : G >= 6 ? G1 : G >= 4 ? G2 : G3
//----<Calculate Signals>------------------------------------------------------------------------//
Get_Signal_Index() =>
Value = 0.0
Buy_Index = 0
Sell_Index = 0
start = 4 - G / 2
end = 4 + G / 2
for x = start to end by 1
Value := Get_Array_Values(a_grid, x)
if iEXTR
Sell_Index := low[1] < Value and high >= Value ? x : Sell_Index
Buy_Index := high[1] > Value and low <= Value ? x : Buy_Index
Buy_Index
else
Sell_Index := close[1] < Value and close >= Value ? x : Sell_Index
Buy_Index := close[1] > Value and close <= Value ? x : Buy_Index
Buy_Index
[Buy_Index, Sell_Index]
[BuyLine_Index, SellLine_Index] = Get_Signal_Index()
//----<Signals>----------------------------------------------------------------------------------//
Buy := BuyLine_Index > 0 ? true : Buy
Sell := SellLine_Index > 0 ? true : Sell
//No repeat trades at current level
Buy := low >= SignalLine[1] - GI ? false : Buy
Sell := high <= SignalLine[1] + GI ? false : Sell
//No trades outside of grid limits
Buy := close > UpperLimit ? false : Buy
Buy := close < LowerLimit ? false : Buy
Sell := close < LowerLimit ? false : Sell
Sell := close > UpperLimit ? false : Sell
//Direction Filter (skip one signal if against market direction)
DIR := iDir == 'up' ? 1 : iDir == 'down' ? -1 : 0
Buy := DIR == -1 and low >= SignalLine[1] - GI * 2 ? false : Buy
Sell := DIR == 1 and high <= SignalLine[1] + GI * 2 ? false : Sell
//Conflicting Signals
if Buy and Sell
Buy := false
Sell := false
LastSignal_Index := LastSignal_Index[1]
LastSignal_Index
//----<Cooldown>---------------------------------------------------------------------------------//
y = 0
for i = 1 to iCool by 1
if Buy[i] or Sell[i]
y := 0
break
y += 1
y
CoolDown = y
Buy := CoolDown < iCool ? false : Buy
Sell := CoolDown < iCool ? false : Sell
//----<Trackers>---------------------------------------------------------------------------------//
LastSignal := Buy ? 1 : Sell ? -1 : LastSignal[1]
LastSignal_Index := Buy ? BuyLine_Index : Sell ? SellLine_Index : LastSignal_Index[1]
SignalLine := Get_Array_Values(a_grid, LastSignal_Index)
//Reset to Center Grid when LMA changes
if iReset
SignalLine := LMA < LMA[1] ? UpperLimit : SignalLine
SignalLine := LMA > LMA[1] ? LowerLimit : SignalLine
SignalLine
BuyLine := Get_Array_Values(a_grid, BuyLine_Index)
SellLine := Get_Array_Values(a_grid, SellLine_Index)
//----<Plot Grids>--------------------------//
color apColor = na
apColor := MA < AP ? color.new(color.red, iGT) : MA > AP ? color.new(color.green, iGT) : apColor[1]
apColor := LMA != LMA[1] ? na : apColor
plot(G >= 8 ? G0 : na, color=GreenGrid)
plot(G >= 6 ? G1 : na, color=GreenGrid)
plot(G >= 4 ? G2 : na, color=GreenGrid)
plot(G >= 2 ? G3 : na, color=GreenGrid)
plot(G4, color=apColor, linewidth=4) // Center
plot(G >= 2 ? G5 : na, color=RedGrid)
plot(G >= 4 ? G6 : na, color=RedGrid)
plot(G >= 6 ? G7 : na, color=RedGrid)
plot(G >= 8 ? G8 : na, color=RedGrid)
//fill
LineAbove = SignalLine == UpperLimit ? SignalLine : SignalLine + GI
LineBelow = SignalLine == LowerLimit ? SignalLine : SignalLine - GI
a = plot(LineAbove, color=color.new(color.red, 100), style=plot.style_circles)
b = plot(LineBelow, color=color.new(color.green, 100), style=plot.style_circles)
boxColor = LastSignal == 1 ? color.new(color.green, iFT) : color.new(color.red, iFT)
if iRFC
boxColor := LastSignal == -1 ? color.new(color.green, iFT) : color.new(color.red, iFT)
boxColor
fill(a, b, color=boxColor)
//----<Plot Signals>-----------------------------------------------------------------------------//
plotchar(Buy and iSS == 'small', 'Buy', color=color.new(LimeGreen, 0), size=size.tiny, location=location.belowbar, char='▲')
plotchar(Sell and iSS == 'small', 'Sell', color=color.new(Crimson, 0), size=size.tiny, location=location.abovebar, char='▼')
plotchar(Buy and iSS == 'large', 'Buy', color=color.new(LimeGreen, 0), size=size.small, location=location.belowbar, char='▲')
plotchar(Sell and iSS == 'large', 'Sell', color=color.new(Crimson, 0), size=size.small, location=location.abovebar, char='▼')
//----<Alerts>-----------------------------------------------------------------------------------//
alertcondition(condition=Buy, title='buy', message='buy')
alertcondition(condition=Sell, title='sell', message='sell')
//-----------------------------------------------------------------------------------------------//
if strategy.position_size >= 0 and Buy
strategy.entry("Long", strategy.long, qty = qty_pos)
if strategy.position_size <= 0 and Sell
strategy.entry("Short", strategy.short, qty = qty_pos)
if strategy.position_size > 0 and Sell
strategy.close("Long", qty = qty_pos)
if strategy.position_size < 0 and Buy
strategy.close("Short", qty = qty_pos)