多级别价格结构识别与公允价值缺口量化交易系统是一种基于价格行为的自动化交易策略,它结合了两个关键的交易概念:变化特征(CHoCH,Change of Character)和公允价值缺口(FVG,Fair Value Gap)。该策略通过识别市场结构变化点和不平衡区域来捕捉高概率交易机会,在价格回撤至公允价值缺口时进场,实现精确的入场和出场控制。这种系统化的方法使交易者能够客观地分析市场,消除情绪因素,并具有明确的风险管理规则。
该量化交易系统基于以下核心原理运作:
价格结构识别:通过波峰波谷(Pivot Points)技术识别市场中的摆动高点和摆动低点,这些点位是市场结构的关键组成部分。系统使用参数化的摆动长度(默认为5个周期)来确定这些关键点位。
变化特征(CHoCH)检测:
公允价值缺口(FVG)识别:
入场逻辑:
风险管理机制:
经过对代码的深入分析,该策略具有以下显著优势:
结构化市场分析:策略基于价格结构变化和市场不平衡原理,而非简单的指标交叉,使其在识别市场转折点方面具有独特优势。
精确的入场时机:通过等待CHoCH后的FVG形成,策略能够在有利的价格水平入场,避免追高杀低,提高入场质量。
自适应风险管理:策略根据实际市场结构自动调整止损位置,而非使用固定点数,这种方法更符合市场实际波动特性。
可视化交易元素:策略提供了全面的可视化功能,包括CHoCH标签、FVG盒子、摆动点和交易线,使交易者能够直观理解市场结构和策略逻辑。
灵活的头寸管理:通过风险百分比自动调整头寸大小,既保护账户资金又能根据波动性自动调整风险敞口。
性能优化设计:代码中包含了清理旧FVG盒子的机制,确保长时间运行时系统性能不会降低。
综合绩效监控:策略提供实时绩效表格,包括策略状态、胜率、利润因子等关键指标,便于交易者评估策略表现。
尽管该策略设计合理,但仍存在一些潜在风险和局限性:
假突破风险:CHoCH信号可能是假突破,导致价格迅速回撤并触发止损。为缓解此风险,可考虑增加确认机制,如等待多根K线确认突破。
间隙风险:在波动性大的市场或隔夜交易中,价格可能跳空超过止损位置,导致实际损失超过预期。建议使用保证止损订单(如有可能)或降低头寸规模。
参数敏感性:策略性能高度依赖于摆动长度、最小CHoCH距离和FVG大小等参数设置。不同市场和时间框架可能需要不同的参数组合,建议进行全面的回测优化。
市场环境依赖:该策略在趋势市场中表现更好,而在盘整市场可能产生频繁的错误信号。考虑添加趋势过滤器或市场状态识别机制。
计算复杂性:策略使用多个数组和条件检查,在较低配置的设备上可能导致性能问题。虽然代码已包含清理机制,但长期运行仍需注意资源消耗。
回撤管理不足:目前策略没有考虑不同市场条件下的头寸规模动态调整,在持续不利环境中可能导致较大回撤。建议实现账户回撤限制和逐步减仓机制。
基于代码分析,提出以下优化方向:
多时间框架确认:引入更高时间框架的市场结构分析,只在主要趋势方向交易。例如,可添加日线趋势过滤器,仅在日线趋势方向一致时执行交易。
动态参数优化:实现基于市场波动性自动调整的参数系统,例如在高波动期间增加最小FVG大小和CHoCH距离要求,低波动期间减小这些参数。
入场优化:
风险管理增强:
市场状态适应:
机器学习增强:引入机器学习算法对历史CHoCH和FVG模式进行分析,识别成功率更高的模式特征,并据此调整入场决策权重。
交易时间过滤:添加交易时间过滤器,避开重大新闻公告和市场开盘/收盘时的高波动期,专注于流动性较好的交易时段。
多级别价格结构识别与公允价值缺口量化交易系统是一个结合了先进价格行为理论的完整交易解决方案。它通过识别市场结构变化(CHoCH)和价格不平衡区域(FVG),在理想的价格水平进场,并采用系统化的风险管理方法保护交易资本。
该策略最大的优势在于其基于实际市场结构的分析方法,而非依赖滞后指标,使其能够更早识别市场转折点。同时,完善的可视化功能和绩效监控系统使交易者能够直观理解策略逻辑并评估其有效性。
虽然存在假突破和参数敏感性等风险,但通过提出的优化方向,尤其是多时间框架确认、动态参数调整和增强的风险管理功能,可以显著提高策略的稳定性和性能。
对于希望采用系统化方法进行交易的投资者,这一策略提供了一个坚实的框架,既吸收了传统价格行为交易的精髓,又利用了量化系统的客观性和纪律性优势。通过持续的参数优化和市场适应性调整,该策略有潜力在各种市场环境中实现稳定的交易业绩。
/*backtest
start: 2024-06-03 00:00:00
end: 2025-06-02 00:00:00
period: 2h
basePeriod: 2h
exchanges: [{"eid":"Futures_Binance","currency":"ETH_USDT"}]
*/
//@version=5
strategy("ICT CHoCH & FVG Strategy - NQ1!", overlay=true, pyramiding=0, calc_on_every_tick=false, calc_on_order_fills=false, max_boxes_count=500, max_lines_count=100, max_labels_count=100)
// ============================================================================
// INPUT PARAMETERS
// ============================================================================
// Strategy Settings
riskRewardRatio = input.float(2.0, title="Risk:Reward Ratio", minval=0.5, maxval=10.0, group="Strategy Settings")
fixedTarget = input.int(40, title="Fixed Target (Ticks)", minval=5, maxval=200, group="Strategy Settings")
useRRTarget = input.bool(true, title="Use Risk:Reward Target", tooltip="If false, uses fixed target", group="Strategy Settings")
riskPercent = input.float(2.0, title="Risk % of Account", minval=0.1, maxval=10.0, group="Strategy Settings")
useAutoSize = input.bool(false, title="Auto Size Positions", tooltip="Size based on risk % and stop distance", group="Strategy Settings")
// Visual Settings
showCHoCH = input.bool(true, title="Show CHoCH Labels", group="Visual Settings")
showFVG = input.bool(true, title="Show FVG Boxes", group="Visual Settings")
showSwings = input.bool(true, title="Show Swing Points", group="Visual Settings")
showTradeLines = input.bool(true, title="Show Entry/SL/TP Lines", group="Visual Settings")
// CHoCH Detection Settings
swingLength = input.int(5, title="Swing Detection Length", minval=2, maxval=20, group="CHoCH Settings")
minCHoCHDistance = input.int(10, title="Min CHoCH Distance (bars)", minval=5, maxval=50, group="CHoCH Settings")
// FVG Settings
minFVGSize = input.float(2.0, title="Min FVG Size (ticks)", minval=0.25, maxval=10.0, group="FVG Settings")
maxFVGAge = input.int(50, title="Max FVG Age (bars)", minval=10, maxval=200, group="FVG Settings")
// ============================================================================
// VARIABLES AND ARRAYS
// ============================================================================
// Swing point detection
var float lastSwingHigh = na
var float lastSwingLow = na
var int lastSwingHighBar = na
var int lastSwingLowBar = na
// CHoCH tracking
var bool bullishCHoCH = false
var bool bearishCHoCH = false
var float chochLevel = na
var int chochBar = na
var bool waitingForFVG = false
// FVG tracking
var array<box> bullishFVGs = array.new<box>()
var array<box> bearishFVGs = array.new<box>()
var float activeFVGTop = na
var float activeFVGBottom = na
var bool lookingForEntry = false
// Trade management
var float stopLossLevel = na
var float takeProfitLevel = na
var bool inPosition = false
// ============================================================================
// HELPER FUNCTIONS
// ============================================================================
// Convert ticks to price for NQ
ticksToPrice(ticks) => ticks * 0.25
// Calculate position size based on risk
calcPositionSize(stopDistance) =>
if useAutoSize and strategy.equity > 0
accountValue = strategy.equity
riskAmount = accountValue * (riskPercent / 100)
stopDistancePrice = stopDistance * syminfo.mintick
math.max(1, math.floor(riskAmount / stopDistancePrice))
else
1
// ============================================================================
// SWING POINT DETECTION
// ============================================================================
// Detect swing highs and lows
swingHigh = ta.pivothigh(high, swingLength, swingLength)
swingLow = ta.pivotlow(low, swingLength, swingLength)
// Update swing points
if not na(swingHigh)
lastSwingHigh := swingHigh
lastSwingHighBar := bar_index - swingLength
if showSwings
label.new(bar_index - swingLength, swingHigh, "SH", style=label.style_triangledown, color=color.red, size=size.tiny)
if not na(swingLow)
lastSwingLow := swingLow
lastSwingLowBar := bar_index - swingLength
if showSwings
label.new(bar_index - swingLength, swingLow, "SL", style=label.style_triangleup, color=color.green, size=size.tiny)
// ============================================================================
// CHoCH DETECTION
// ============================================================================
// Check for bullish CHoCH (break above prior swing high after making lower low)
bullishCHoCHCondition = not na(lastSwingHigh) and not na(lastSwingLow) and
high > lastSwingHigh and
lastSwingLow < lastSwingHigh and
bar_index - lastSwingHighBar > minCHoCHDistance and
strategy.position_size == 0
// Check for bearish CHoCH (break below prior swing low after making higher high)
bearishCHoCHCondition = not na(lastSwingHigh) and not na(lastSwingLow) and
low < lastSwingLow and
lastSwingHigh > lastSwingLow and
bar_index - lastSwingLowBar > minCHoCHDistance and
strategy.position_size == 0
// Process CHoCH signals
if bullishCHoCHCondition and not bullishCHoCH
bullishCHoCH := true
bearishCHoCH := false
chochLevel := lastSwingHigh
chochBar := bar_index
waitingForFVG := true
lookingForEntry := false
if bearishCHoCHCondition and not bearishCHoCH
bearishCHoCH := true
bullishCHoCH := false
chochLevel := lastSwingLow
chochBar := bar_index
waitingForFVG := true
lookingForEntry := false
// ============================================================================
// FVG DETECTION
// ============================================================================
// Check for FVG formation (3-candle pattern)
if bar_index >= 2
// Bullish FVG: low[0] > high[2]
bullishFVG = low[0] > high[2] and (low[0] - high[2]) >= ticksToPrice(minFVGSize)
// Bearish FVG: high[0] < low[2]
bearishFVG = high[0] < low[2] and (low[2] - high[0]) >= ticksToPrice(minFVGSize)
// Process bullish FVG after bullish CHoCH
if bullishFVG and bullishCHoCH and waitingForFVG and bar_index > chochBar
fvgTop = low[0]
fvgBottom = high[2]
// Set active FVG for entry
activeFVGTop := fvgTop
activeFVGBottom := fvgBottom
waitingForFVG := false
lookingForEntry := true
// Process bearish FVG after bearish CHoCH
if bearishFVG and bearishCHoCH and waitingForFVG and bar_index > chochBar
fvgTop = low[2]
fvgBottom = high[0]
// Set active FVG for entry
activeFVGTop := fvgTop
activeFVGBottom := fvgBottom
waitingForFVG := false
lookingForEntry := true
// ============================================================================
// ENTRY LOGIC
// ============================================================================
// Long entry: price touches bullish FVG after bullish CHoCH
longCondition = lookingForEntry and bullishCHoCH and
not na(activeFVGTop) and not na(activeFVGBottom) and
low <= activeFVGTop and high >= activeFVGBottom and
strategy.position_size == 0
// Short entry: price touches bearish FVG after bearish CHoCH
shortCondition = lookingForEntry and bearishCHoCH and not na(activeFVGTop) and not na(activeFVGBottom) and low <= activeFVGTop and high >= activeFVGBottom and strategy.position_size == 0
// Process long entries
if longCondition
var float entryPrice = na
var float stopLoss = na
var float takeProfit = na
entryPrice := math.avg(activeFVGTop, activeFVGBottom)
stopLoss := lastSwingLow
stopDistance = entryPrice - stopLoss
if useRRTarget
takeProfit := entryPrice + (stopDistance * riskRewardRatio)
else
takeProfit := entryPrice + ticksToPrice(fixedTarget)
// Calculate position size
qty = calcPositionSize(stopDistance / syminfo.mintick)
// Enter trade
strategy.entry("Long", strategy.long, qty=qty)
strategy.exit("Long Exit", "Long", stop=stopLoss, limit=takeProfit)
// Update tracking
stopLossLevel := stopLoss
takeProfitLevel := takeProfit
inPosition := true
lookingForEntry := false
// Reset CHoCH state
bullishCHoCH := false
activeFVGTop := na
activeFVGBottom := na
// Process short entries
if shortCondition
var float entryPrice = na
var float stopLoss = na
var float takeProfit = na
entryPrice := math.avg(activeFVGTop, activeFVGBottom)
stopLoss := lastSwingHigh
stopDistance = stopLoss - entryPrice
if useRRTarget
takeProfit := entryPrice - (stopDistance * riskRewardRatio)
else
takeProfit := entryPrice - ticksToPrice(fixedTarget)
// Calculate position size
qty = calcPositionSize(stopDistance / syminfo.mintick)
// Enter trade
strategy.entry("Short", strategy.short, qty=qty)
strategy.exit("Short Exit", "Short", stop=stopLoss, limit=takeProfit)
// Update tracking
stopLossLevel := stopLoss
takeProfitLevel := takeProfit
inPosition := true
lookingForEntry := false
// Reset CHoCH state
bearishCHoCH := false
activeFVGTop := na
activeFVGBottom := na
// ============================================================================
// POSITION MANAGEMENT
// ============================================================================
// Reset position state when trade is closed
if inPosition and strategy.position_size == 0
inPosition := false
stopLossLevel := na
takeProfitLevel := na
// ============================================================================
// VISUAL SIGNALS
// ============================================================================
// Plot entry signals
plotshape(longCondition, title="Long Entry", location=location.belowbar, color=color.green,
style=shape.triangleup, size=size.normal)
plotshape(shortCondition, title="Short Entry", location=location.abovebar, color=color.red,
style=shape.triangledown, size=size.normal)
// Plot active stop loss and take profit levels
plot(inPosition ? stopLossLevel : na, title="Stop Loss", color=color.red, linewidth=2, style=plot.style_linebr)
plot(inPosition ? takeProfitLevel : na, title="Take Profit", color=color.green, linewidth=2, style=plot.style_linebr)
// ============================================================================
// CLEANUP
// ============================================================================
// Clean up old FVG boxes (helps with performance)
if bar_index % 100 == 0
while array.size(bullishFVGs) > 20
box.delete(array.shift(bullishFVGs))
while array.size(bearishFVGs) > 20
box.delete(array.shift(bearishFVGs))
// ============================================================================
// ALERTS
// ============================================================================
// Alert conditions
alertcondition(longCondition, title="Long Entry Signal", message="ICT Strategy: Long entry at FVG - SL: {{strategy.position_avg_price}}")
alertcondition(shortCondition, title="Short Entry Signal", message="ICT Strategy: Short entry at FVG - SL: {{strategy.position_avg_price}}")