
Chiến lược này là một hệ thống giao dịch kết hợp Đường trung bình động theo hàm mũ (EMA) và Chu kỳ khối lượng tích lũy (CVP). Phương pháp này nắm bắt các điểm ngoặt trong xu hướng thị trường bằng cách phân tích sự giao nhau giữa đường trung bình động hàm mũ của giá và giá tích lũy theo khối lượng. Chiến lược này có bộ lọc thời gian tích hợp có thể giới hạn giờ giao dịch và hỗ trợ tự động đóng các vị thế vào cuối thời gian giao dịch. Chiến lược này cung cấp hai phương pháp thoát khác nhau: thoát chéo ngược và thoát CVP tùy chỉnh, giúp linh hoạt và thích ứng hơn.
Logic cốt lõi của chiến lược này dựa trên những tính toán quan trọng sau:
Đây là chiến lược giao dịch định lượng có cấu trúc hoàn chỉnh và logic rõ ràng. Bằng cách kết hợp những ưu điểm của EMA và CVP, một hệ thống giao dịch được tạo ra có thể nắm bắt xu hướng trong khi tập trung vào kiểm soát rủi ro. Chiến lược này có khả năng tùy chỉnh cao và phù hợp để sử dụng trong nhiều môi trường thị trường khác nhau. Thông qua việc thực hiện các đề xuất tối ưu hóa, hiệu suất của chiến lược sẽ có thể được cải thiện hơn nữa.
/*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)