本策略是一个结合了指数移动平均线(EMA)和累积成交量周期(CVP)的交易系统。它通过分析价格的指数移动平均与累积成交量加权价格的交叉来捕捉市场趋势的转折点。策略内置了时间过滤器,可以限定交易时段,并且支持在交易时段结束时自动平仓。策略提供了两种不同的出场方式:反向交叉出场和自定义CVP出场,使其具有较强的灵活性和适应性。
策略的核心逻辑基于以下几个关键计算: 1. 计算平均价格(AVWP):将最高价、最低价和收盘价的算术平均值与成交量相乘。 2. 计算累积成交量周期值:在设定的周期内累加成交量加权价格并除以累积成交量。 3. 分别计算收盘价的EMA和CVP的EMA。 4. 当价格EMA向上穿越CVP的EMA时产生做多信号;当价格EMA向下穿越CVP的EMA时产生做空信号。 5. 出场信号可以是反向交叉信号,也可以是基于自定义CVP周期的交叉信号。
这是一个结构完整、逻辑清晰的量化交易策略。通过结合EMA和CVP的优势,创造了一个既能捕捉趋势又注重风险控制的交易系统。策略的可定制性强,适合在不同的市场环境中使用。通过优化建议的实施,策略的性能还有进一步提升的空间。
/*backtest
start: 2019-12-23 08:00:00
end: 2025-01-04 08:00:00
period: 1d
basePeriod: 1d
exchanges: [{"eid":"Futures_Binance","currency":"BTC_USDT"}]
*/
//@version=5
// © sapphire_edge
// # ========================================================================= #
// #
// # _____ __ _ ______ __
// # / ___/____ _____ ____ / /_ (_)_______ / ____/___/ /___ ____
// # \__ \/ __ `/ __ \/ __ \/ __ \/ / ___/ _ \ / __/ / __ / __ `/ _ \
// # ___/ / /_/ / /_/ / /_/ / / / / / / / __/ / /___/ /_/ / /_/ / __/
// # /____/\__,_/ .___/ .___/_/ /_/_/_/ \___/ /_____/\__,_/\__, /\___/
// # /_/ /_/ /____/
// #
// # ========================================================================= #
strategy(shorttitle="⟡Sapphire⟡ EMA/CVP", title="[Sapphire] EMA/CVP Strategy", initial_capital= 50000, currency= currency.USD,default_qty_value = 1,commission_type= strategy.commission.cash_per_contract,overlay= true )
// # ========================================================================= #
// # // Settings Menu //
// # ========================================================================= #
// -------------------- Main Settings -------------------- //
groupEMACVP = "EMA / Cumulative Volume Period"
tradeDirection = input.string(title='Trade Direction', defval='LONG', options=['LONG', 'SHORT'], group=groupEMACVP)
emaLength = input.int(25, title='EMA Length', minval=1, maxval=200, group=groupEMACVP)
cumulativePeriod = input.int(100, title='Cumulative Volume Period', minval=1, maxval=200, step=5, group=groupEMACVP)
exitType = input.string(title="Exit Type", defval="Crossover", options=["Crossover", "Custom CVP" ], group=groupEMACVP)
cumulativePeriodForClose = input.int(50, title='Cumulative Period for Close Signal', minval=1, maxval=200, step=5, group=groupEMACVP)
showSignals = input.bool(true, title="Show Signals", group=groupEMACVP)
signalOffset = input.int(5, title="Signal Vertical Offset", group=groupEMACVP)
// -------------------- Time Filter Inputs -------------------- //
groupTimeOfDayFilter = "Time of Day Filter"
useTimeFilter1 = input.bool(false, title="Enable Time Filter 1", group=groupTimeOfDayFilter)
startHour1 = input.int(0, title="Start Hour (24-hour format)", minval=0, maxval=23, group=groupTimeOfDayFilter)
startMinute1 = input.int(0, title="Start Minute", minval=0, maxval=59, group=groupTimeOfDayFilter)
endHour1 = input.int(23, title="End Hour (24-hour format)", minval=0, maxval=23, group=groupTimeOfDayFilter)
endMinute1 = input.int(45, title="End Minute", minval=0, maxval=59, group=groupTimeOfDayFilter)
closeAtEndTimeWindow = input.bool(false, title="Close Trades at End of Time Window", group=groupTimeOfDayFilter)
// -------------------- Trading Window -------------------- //
isWithinTradingWindow(startHour, startMinute, endHour, endMinute) =>
nyTime = timestamp("America/New_York", year, month, dayofmonth, hour, minute)
nyHour = hour(nyTime)
nyMinute = minute(nyTime)
timeInMinutes = nyHour * 60 + nyMinute
startInMinutes = startHour * 60 + startMinute
endInMinutes = endHour * 60 + endMinute
timeInMinutes >= startInMinutes and timeInMinutes <= endInMinutes
timeCondition = (useTimeFilter1 ? isWithinTradingWindow(startHour1, startMinute1, endHour1, endMinute1) : true)
// Check if the current bar is the last one within the specified time window
isEndOfTimeWindow() =>
nyTime = timestamp("America/New_York", year, month, dayofmonth, hour, minute)
nyHour = hour(nyTime)
nyMinute = minute(nyTime)
timeInMinutes = nyHour * 60 + nyMinute
endInMinutes = endHour1 * 60 + endMinute1
timeInMinutes == endInMinutes
// Logic to close trades if the time window ends
if timeCondition and closeAtEndTimeWindow and isEndOfTimeWindow()
strategy.close_all(comment="Closing trades at end of time window")
// # ========================================================================= #
// # // Calculations //
// # ========================================================================= #
avgPrice = (high + low + close) / 3
avgPriceVolume = avgPrice * volume
cumulPriceVolume = math.sum(avgPriceVolume, cumulativePeriod)
cumulVolume = math.sum(volume, cumulativePeriod)
cumValue = cumulPriceVolume / cumulVolume
cumulPriceVolumeClose = math.sum(avgPriceVolume, cumulativePeriodForClose)
cumulVolumeClose = math.sum(volume, cumulativePeriodForClose)
cumValueClose = cumulPriceVolumeClose / cumulVolumeClose
emaVal = ta.ema(close, emaLength)
emaCumValue = ta.ema(cumValue, emaLength)
// # ========================================================================= #
// # // Signal Logic //
// # ========================================================================= #
// Strategy Entry Conditions
longEntryCondition = ta.crossover(emaVal, emaCumValue) and tradeDirection == 'LONG'
shortEntryCondition = ta.crossunder(emaVal, emaCumValue) and tradeDirection == 'SHORT'
// User-Defined Exit Conditions
longExitCondition = false
shortExitCondition = false
if exitType == "Crossover"
longExitCondition := ta.crossunder(emaVal, emaCumValue)
shortExitCondition := ta.crossover(emaVal, emaCumValue)
if exitType == "Custom CVP"
emaCumValueClose = ta.ema(cumValueClose, emaLength)
longExitCondition := ta.crossunder(emaVal, emaCumValueClose)
shortExitCondition := ta.crossover(emaVal, emaCumValueClose)
// # ========================================================================= #
// # // Strategy Management //
// # ========================================================================= #
// Strategy Execution
if longEntryCondition and timeCondition
strategy.entry('Long', strategy.long)
label.new(bar_index, high - signalOffset, "◭", style=label.style_label_up, color = color.rgb(119, 0, 255, 20), textcolor=color.white)
if shortEntryCondition and timeCondition
strategy.entry('Short', strategy.short)
label.new(bar_index, low + signalOffset, "⧩", style=label.style_label_down, color = color.rgb(255, 85, 0, 20), textcolor=color.white)
if strategy.position_size > 0 and longExitCondition
strategy.close('Long')
if strategy.position_size < 0 and shortExitCondition
strategy.close('Short')
// # ========================================================================= #
// # // Plots and Charts //
// # ========================================================================= #
plot(emaVal, title='EMA', color=color.new(color.green, 25))
plot(emaCumValue, title='Cumulative EMA', color=color.new(color.purple, 35))
fill(plot(emaVal), plot(emaCumValue), color=emaVal > emaCumValue ? #008ee6 : #d436a285, title='EMA and Cumulative Area', transp=70)