该策略是一个基于指数移动平均线(EMA)交叉信号的智能美元成本平均(DCA)策略,结合了自适应波动性的安全订单(SO)部署和创新的双轨止损机制。它在上升趋势确认时进入市场,然后根据市场波动性自动部署额外的安全订单,同时使用标准跟踪止损和利润锁定跟踪系统来保护收益。该策略适合在波动较大的市场环境中运行,特别是针对1小时时间周期进行了优化,使用总资金为4000美元进行交易操作。
此策略的核心逻辑围绕以下几个关键组件展开:
趋势识别系统: 使用快速EMA(默认9周期)和慢速EMA(默认21周期)的交叉来识别潜在的上升趋势。当快速EMA向上穿越慢速EMA时,系统确认上升趋势并触发基础入场订单。
多层次DCA入场系统: 策略采用三级入场机制:
波动性自适应机制: 安全订单的触发价格可以基于ATR(平均真实范围)指标动态计算,使策略能够根据市场当前波动情况自动调整入场位置。用户可选择使用ATR乘数(默认SO1为1.2倍ATR,SO2为2.5倍ATR)或固定百分比下跌(默认SO1为4%,SO2为8%)来计算安全订单的触发点。
双轨止损保护系统:
冷却期机制: 基础订单执行后实施冷却期(默认4根K线),防止在短时间内过度交易。
经过分析,该策略展现出以下显著优势:
自适应性强: 通过ATR计算安全订单触发价格,使策略能够智能适应不同的市场波动环境,在高波动期适当拉大安全订单间距,在低波动期收紧间距。
资金管理优化: 采用递增的资金分配方式(1000美元→1250美元→1750美元),符合”金字塔式”仓位管理原则,让策略在价格下跌时能够以更大资金规模获取更优的平均入场价格。
双层保护机制: 创新的双轨止损系统既提供了基本的下行风险保护,又能在盈利时自动切换至更保守的止损模式,有效平衡了利润最大化与风险控制。
定制化灵活性: 所有关键参数均可自定义,包括EMA周期、ATR长度、安全订单间距、止损比例和订单大小,允许交易者根据个人风险偏好和市场条件进行优化。
集成性: 策略内置了警报条件格式化为JSON消息,便于与第三方自动交易平台(如3Commas)集成,实现全自动化交易执行。
尽管该策略设计全面,仍存在以下潜在风险和挑战:
趋势反转风险: 策略依赖EMA交叉信号,在市场快速变化或震荡市场中可能产生错误信号,导致不必要的入场。解决方法是调整EMA周期长度或增加额外的确认指标。
资金消耗风险: 在持续下跌的市场中,即使部署了所有安全订单,平均入场价格仍可能远高于市场价格,导致长期亏损。建议设置最大亏损限制或总体仓位规模限制。
过度交易风险: 在波动剧烈的市场中,EMA可能频繁交叉,触发过多交易。尽管内置了冷却期机制,可能需要进一步优化或添加额外的交易频率限制。
双轨止损互相干扰: 在某些市场情况下,两种止损机制可能相互干扰,导致过早退出或重复信号。应定期回测并调整这两个止损参数之间的平衡。
参数优化困难: 策略的多个参数需要相互协调才能达到最佳效果,增加了参数优化的复杂性。建议使用回测优化工具进行全面参数分析。
基于对代码的深入分析,以下是该策略的潜在优化方向:
引入多重趋势确认机制: 目前策略仅依赖单一的EMA交叉信号,可考虑添加额外的趋势确认指标,如RSI、MACD或更长周期的趋势判断,以减少错误信号。这样做能显著降低假突破带来的风险。
动态资金分配系统: 当前策略使用固定美元金额作为订单大小,可优化为基于市场波动性或账户权益的动态调整系统,确保在不同市场条件下维持适当的风险暴露水平。
优化的止损退出策略: 可以开发更复杂的止损逻辑,例如基于市场波动性的自适应跟踪止损,或整合动量和成交量指标来优化退出决策,避免在短期波动中过早退出。
回撤控制增强: 添加总体回撤限制功能,当策略达到预设的最大回撤百分比时自动暂停新订单或关闭现有头寸,防止在极端市场条件下的灾难性损失。
周期优化系统: 开发自动周期优化功能,使策略能够基于近期市场条件自动调整EMA长度、ATR周期和其他时间相关参数,以适应市场状态的变化。
“智能波动跟踪型DCA策略与双轨止损系统”是一个设计精良的量化交易方案,特别适合在波动性市场中捕捉上升趋势并管理风险。它巧妙地将趋势跟踪、美元成本平均法和波动率自适应机制相结合,并通过创新的双轨止损系统保护收益。
该策略的核心优势在于其适应性和风险管理平衡,能够在不同市场环境中自动调整入场和出场决策。通过使用ATR来动态计算安全订单触发点,策略可以根据实时市场条件作出智能响应,而非依赖预设的静态参数。
虽然存在趋势识别和资金管理方面的潜在风险,但这些可以通过提出的优化方向得到有效缓解。特别是引入多重趋势确认和动态资金分配系统,将显著提升策略的稳健性和长期绩效。
对于寻求在波动性市场中系统性交易方法的量化交易者,该策略提供了一个全面、可扩展的框架,既能捕捉上升趋势的机会,又能在不利市场条件下提供充分的风险保护。
/*backtest
start: 2025-03-14 00:00:00
end: 2025-04-02 00:00:00
period: 1h
basePeriod: 1h
exchanges: [{"eid":"Futures_Binance","currency":"ETH_USDT"}]
*/
//@version=5
strategy(
title="BONK/USD (1H) - $4k DCA + Dual Trailing + Date Filter", // Updated Title
overlay=true,
initial_capital=4000,
currency=currency.USD,
default_qty_type=strategy.fixed,
default_qty_value=0, // Quantity calculated dynamically based on USD value
commission_type=strategy.commission.percent, // Example: Add commission settings
commission_value=0.1 // Example: 0.1% commission
)
// 1) USER INPUTS (Defaults adjusted for 1H timeframe - REQUIRES BACKTESTING/TUNING)
// --- Trend ---
fastMALen = input.int(9, title="Fast EMA Length (Default for 1H)")
slowMALen = input.int(21, title="Slow EMA Length (Default for 1H)")
// --- Trailing Stops ---
trailStopPerc = input.float(8.0, title="Standard Trailing Stop (%) (Default for 1H)", minval=0.1) / 100
lockInThreshold = input.float(2.5, title="Profit Lock-In Trigger (%) (Default for 1H)", minval=0.1) / 100
lockInTrailPct = input.float(1.5, title="Lock-In Trail (%) after Trigger (Default for 1H)", minval=0.1) / 100
// --- Safety Orders (SO) ---
useATRSpacing = input.bool(true, title="Use ATR-Based Spacing?")
atrLength = input.int(14, title="ATR Length", minval=1)
atrSo1Multiplier = input.float(1.2, title="ATR SO1 Multiplier (Default for 1H)", minval=0.1)
atrSo2Multiplier = input.float(2.5, title="ATR SO2 Multiplier (Default for 1H)", minval=0.1)
// --- Fallback SO Spacing (if not using ATR) ---
fallbackSo1Perc = input.float(4.0, title="Fallback SO1 Drop (%) (Default for 1H)", minval=0.1) / 100
fallbackSo2Perc = input.float(8.0, title="Fallback SO2 Drop (%) (Default for 1H)", minval=0.1) / 100
// --- Entry Cooldown ---
cooldownBars = input.int(4, "Cooldown Bars After Base Entry (Default for 1H)", minval=0)
// --- Order Sizes in USD ---
baseUsd = input.float(1000.0, title="Base Order Size (USD)", minval=1.0)
so1Usd = input.float(1250.0, title="Safety Order 1 Size (USD)", minval=1.0)
so2Usd = input.float(1750.0, title="Safety Order 2 Size (USD)", minval=1.0)
// 2) CALCULATIONS
// --- Trend & Reversal Detection ---
fastMA = ta.ema(close, fastMALen)
slowMA = ta.ema(close, slowMALen)
trendUp = ta.crossover(fastMA, slowMA)
trendDown = ta.crossunder(fastMA, slowMA)
// --- ATR Value ---
atrValue = ta.atr(atrLength)
// 3) BASE ENTRY LOGIC
// Base Buy Signal: EMA crossover
baseBuySignal = trendUp
var int lastBuyBar = na // Tracks the bar index of the last base entry
inCooldown = not na(lastBuyBar) and (bar_index - lastBuyBar < cooldownBars)
var float baseEntryPrice = na // Stores the price of the initial base entry for SO calculations
// --- Execute Base Entry ---
// Added 'inDateRange' to the condition
if baseBuySignal and strategy.position_size == 0 and not inCooldown
baseQty = baseUsd / close // Calculate quantity based on USD
strategy.entry("Base Order", strategy.long, qty=baseQty, comment="Base Entry")
baseEntryPrice := close
lastBuyBar := bar_index
// 4) SAFETY ORDERS LOGIC
// --- Calculate SO Trigger Prices ---
float so1TriggerPrice = na
float so2TriggerPrice = na
if not na(baseEntryPrice) // Only calculate if a base order has been placed
so1TriggerPrice := useATRSpacing ?
(baseEntryPrice - atrValue * atrSo1Multiplier) :
(baseEntryPrice * (1 - fallbackSo1Perc))
so2TriggerPrice := useATRSpacing ?
(baseEntryPrice - atrValue * atrSo2Multiplier) :
(baseEntryPrice * (1 - fallbackSo2Perc))
// --- Conditions for SO Execution ---
// Added 'inDateRange' check
// Ensure base order exists, price trigger hit, and the specific SO hasn't filled yet
bool so1Condition = not na(baseEntryPrice) and strategy.position_size > 0 and close <= so1TriggerPrice and strategy.opentrades == 1
bool so2Condition = not na(baseEntryPrice) and strategy.position_size > 0 and close <= so2TriggerPrice and strategy.opentrades == 2
// --- Execute SO1 ---
if so1Condition
so1Qty = so1Usd / close // Calculate quantity based on USD
strategy.entry("Safety Order 1", strategy.long, qty=so1Qty, comment="SO1")
// --- Execute SO2 ---
if so2Condition
so2Qty = so2Usd / close // Calculate quantity based on USD
strategy.entry("Safety Order 2", strategy.long, qty=so2Qty, comment="SO2")
// 5) AVERAGE ENTRY PRICE
// Use the built-in variable for the average price of the open position
avgEntryPrice = strategy.position_avg_price
// 6) DUAL TRAILING STOP LOGIC
// Variables to track trailing stop levels and states
var float highestSinceEntry = na
var float trailStopPrice = na
var bool stopHitNormal = false
var bool lockInTriggered = false
var float lockInPeak = na
var float lockInStopPrice = na
var bool stopHitLockIn = false
// --- Update Trailing Logic when in a Position ---
if strategy.position_size > 0
// --- Standard Trail ---
highestSinceEntry := na(highestSinceEntry) ? close : math.max(highestSinceEntry, close)
trailStopPrice := highestSinceEntry * (1 - trailStopPerc)
stopHitNormal := close < trailStopPrice
// --- Lock-In Trail ---
if not lockInTriggered and close >= avgEntryPrice * (1 + lockInThreshold)
lockInTriggered := true
lockInPeak := close
if lockInTriggered
lockInPeak := math.max(lockInPeak, close)
lockInStopPrice := lockInPeak * (1 - lockInTrailPct)
stopHitLockIn := close < lockInStopPrice
else
stopHitLockIn := false
lockInStopPrice := na
// --- Reset Variables when Flat ---
else
highestSinceEntry := na
trailStopPrice := na
stopHitNormal := false
lockInTriggered := false
lockInPeak := na
lockInStopPrice := na
stopHitLockIn := false
baseEntryPrice := na
// lastBuyBar is intentionally NOT reset here, cooldown depends on it
// 7) EXIT CONDITIONS
// Added 'inDateRange' check
// Exit if either trailing stop is hit OR if the trend reverses downward, within the active date range
exitCondition = (stopHitNormal or stopHitLockIn or trendDown) and strategy.position_size > 0
if exitCondition
strategy.close_all(comment="Exit: SL / LockIn / TrendDown")
// 8) ALERT CONDITIONS (Potential 3Commas Integration)
// WARNING: Verify and adapt these JSON message strings for your specific 3Commas bot configuration!
// The required format ('action', parameters, etc.) can vary.
// Added 'inDateRange[1]' check for Base Alert
alertcondition(inDateRange[1] and baseBuySignal and strategy.position_size[1] == 0 and not inCooldown[1],
title="Base Buy Alert",
message='{"action":"start_deal","order":"base"} // Verify/Adapt JSON for your 3Commas bot!')
// Added 'inDateRange' check for SO1 Alert
alertcondition(so1Condition, title="SO1 Alert",
message='{"action":"add_funds","order":"so1"} // Verify/Adapt JSON for your 3Commas bot!')
// Added 'inDateRange' check for SO2 Alert
alertcondition(so2Condition, title="SO2 Alert",
message='{"action":"add_funds","order":"so2"} // Verify/Adapt JSON for your 3Commas bot!')
// Added 'inDateRange' check for Exit Alert
alertcondition(exitCondition, title="Exit Alert",
message='{"action":"close_at_market_price"} // Verify/Adapt JSON for your 3Commas bot!')
// 9) PLOTS & DEBUG TABLE
// --- Plot MAs ---
plot(fastMA, color=color.new(color.green, 0), title="Fast EMA", linewidth=2)
plot(slowMA, color=color.new(color.red, 0), title="Slow EMA", linewidth=2)
// --- Plot Trailing Stops ---
plot(strategy.position_size > 0 ? trailStopPrice : na, color=color.new(color.orange, 0), title="Standard Trailing Stop", style=plot.style_linebr, linewidth=2)
plot(lockInTriggered ? lockInStopPrice : na, color=color.new(color.fuchsia, 0), title="Lock-In Trailing Stop", style=plot.style_linebr, linewidth=2)
// --- Debug Info Table ---
var table tradeInfo = table.new(position=position.bottom_right, columns=2, rows=10, border_width=1)