
This strategy is an advanced trend-following system that combines dual Exponential Moving Average (EMA) filters with intelligent range and noise detection mechanisms to provide clear, actionable trading signals. Its core design philosophy is to avoid choppy markets, enhance trading precision, and adapt to different market conditions. The strategy determines trend direction through the crossover of EMA High and EMA Low lines, while employing range filters and volatility filters to avoid trading in sideways or low-volatility environments, significantly improving trading success rates.
The core mechanism of the strategy is based on several key components:
Dual EMA Filtering System: The strategy utilizes two exponential moving averages (EMA of high prices and EMA of low prices) to determine market trends. When price is simultaneously above both EMA lines, a long signal is generated; when price is below both EMA lines, a short signal is generated. This dual confirmation mechanism effectively reduces false breakouts.
Range Detection Mechanism: The strategy employs a range identification algorithm based on price range percentage. When the market enters a sideways consolidation phase (i.e., price fluctuation range is less than the set threshold), trading is automatically paused. The system continuously monitors the number of consecutive range bars and only activates the range filter when it confirms the market is in a true range state, avoiding missing initial breakout opportunities.
Volatility Filter: By calculating the ATR (Average True Range) as a percentage of the current price, the strategy can identify low-volatility environments and avoid trading under these conditions. This mechanism ensures trading only occurs when the market has sufficient momentum.
One Trade Per Trend Principle: The strategy implements a trend state machine that ensures only one trade is executed in the same trend direction until the trend direction changes. This prevents overtrading and signal repetition within the same trend.
Unbroken Range Visualization: The strategy can detect and display consolidation zones that may lead to breakouts, helping traders identify potential high-probability trading opportunities.
Dynamic Risk Management: The strategy provides ATR-based stop losses or fixed percentage stop losses, as well as optional Parabolic SAR trailing stops, making risk management more flexible and adaptive to market changes.
High Adaptability: The strategy automatically adapts to different market conditions, capturing trends in trending markets while staying on the sidelines in choppy markets. This adaptability enables it to maintain robustness across various market environments.
Multiple Filtering Mechanisms: By combining trend, range, and volatility triple filtering, the strategy significantly improves the quality of trading signals, reducing false signals and fake breakout trades.
Intelligent Volatility Adjustment: The strategy dynamically adjusts position size based on market volatility, reducing risk exposure in high-volatility environments and maximizing profit potential in moderately volatile environments.
Comprehensive Visualization Tools: The strategy provides rich visual aids, including range markers, unbroken range boxes, EMA lines, and SAR points, allowing traders to intuitively understand market conditions and strategy logic.
Flexible Risk Control: It supports multiple stop-loss strategies (fixed percentage, ATR multiplier, SAR trailing), enabling traders to choose the most suitable risk management method based on personal risk preferences and market characteristics.
One Trade Principle: The trend state machine ensures only one trade is executed in each trend direction, preventing overtrading and excessive exposure to single-direction risk.
Trend Reversal Delay: Due to the use of EMA as the primary trend indicator, the strategy may react slowly to rapid trend reversals, resulting in some drawdown during the initial phase of reversals. The solution is to adjust the EMA length parameter, using shorter EMA lengths in more volatile markets.
Inefficiency in Sideways Markets: Although the strategy is designed with a range filter, it may result in extended periods without trading opportunities in prolonged sideways markets, affecting capital utilization efficiency. The solution is to incorporate multi-timeframe analysis or rotate the strategy between different markets.
Parameter Optimization Dependency: Strategy performance is highly dependent on parameter settings such as EMA length, range threshold, and ATR multiplier. Different markets and timeframes may require different parameter combinations. It is recommended to optimize parameters through backtesting on specific markets and timeframes.
Sudden Large Volatility Risk: In cases of price gaps caused by sudden market events (such as major news releases), stop losses may not execute at expected prices, resulting in actual losses exceeding expectations. It is recommended to use additional money management rules to limit risk exposure per trade.
Over-reliance on Technical Indicators: The strategy is entirely based on technical indicators, ignoring fundamental factors. Pure technical analysis may fail during significant fundamental changes. It is recommended to combine fundamental analysis or set up a risk event calendar, reducing positions or pausing trading before important economic data releases.
Multi-Timeframe Confirmation System: Introducing multi-timeframe analysis can significantly improve strategy accuracy. It is recommended to add trend confirmation conditions from higher timeframes, executing trades only when the higher timeframe trend direction aligns with the current trading direction. This can reduce counter-trend trading and improve win rates.
Dynamic Parameter Adaptation: The strategy can integrate adaptive parameter adjustment mechanisms, automatically adjusting EMA length, range threshold, and ATR multiplier based on market volatility and trend strength. This will enable the strategy to better adapt to different market phases.
Integration of Machine Learning Models: Introducing machine learning models to optimize entry timing and predict range breakout direction can significantly enhance strategy performance. For example, using classification algorithms to predict true versus false breakouts, or using regression models to predict price targets after breakouts.
Improved Volatility Filter: The current volatility filter is based on simple ATR percentage thresholds and can be upgraded to a relative volatility indicator, comparing current volatility with historical volatility distribution to more accurately identify truly low-volatility environments.
Add Volume Confirmation: Adding volume confirmation conditions when generating trading signals and executing trades only when price breakouts are accompanied by volume increases can reduce false breakout risks. This improvement is particularly suitable for stock and commodity markets.
Optimize Money Management Algorithms: Integrating the Kelly Criterion or other advanced money management algorithms into the strategy to dynamically adjust position size based on historical win rates and profit/loss ratios can achieve long-term profit maximization and risk minimization.
The Dual Exponential Moving Average Trend Following Strategy with Smart Volatility Filtering is a comprehensive, robust trading system that effectively improves trading signal quality and success rates by combining trend following, range detection, and volatility filtering techniques. Its unique one-trade-per-trend principle and dynamic risk management mechanisms maintain good profitability while controlling risk. The strategy’s multiple filtering mechanisms and visualization tools make trading decisions more intuitive and well-founded.
Although the strategy also has risks such as trend reversal delays and parameter dependencies, these risks can be effectively managed through the proposed optimization directions, such as multi-timeframe confirmation, dynamic parameter adaptation, and machine learning model integration. With appropriate parameter optimization and risk management, this strategy can maintain stable performance under various market conditions, making it a trading system worthy of long-term use and continuous improvement.
/*backtest
start: 2024-08-01 00:00:00
end: 2025-07-30 08:00:00
period: 1d
basePeriod: 1d
exchanges: [{"eid":"Futures_Binance","currency":"ETH_USDT"}]
*/
//@version=5
strategy("Dubic EMA Strategy", overlay=true,
default_qty_type=strategy.percent_of_equity,
default_qty_value=100,
initial_capital=10000,
currency=currency.USD,
commission_value=0.1,
pyramiding=0,
calc_on_every_tick=true)
// Inputs
ema_length = input.int(40, "EMA Length")
tp_percent = input.float(2.0, "Take Profit %", step=0.1, minval=0.1) / 100
sl_offset = input.float(0.5, "Stop Loss Offset %", step=0.1, minval=0.1) / 100
// Toggles for SL/TP
use_take_profit = input.bool(true, "Use Take Profit")
use_stop_loss = input.bool(true, "Use Stop Loss")
// Range Detection
range_length = input.int(20, "Range Detection Length", minval=5)
range_threshold = input.float(2.0, "Range Threshold %", step=0.1) / 100
min_range_bars = input.int(3, "Min Range Bars", minval=1)
// Parabolic SAR
use_parabolic_sar = input.bool(true, "Use Parabolic SAR Trailing Stop")
sar_start = input.float(0.02, "SAR Start", step=0.01)
sar_increment = input.float(0.02, "SAR Increment", step=0.01)
sar_max = input.float(0.2, "SAR Maximum", step=0.01)
// ATR Stop Configuration
use_atr_stop = input.bool(true, "Use ATR Stop Instead of Fixed %")
atr_length = input.int(14, "ATR Length")
atr_mult = input.float(1.5, "ATR Multiplier", minval=0.5)
// Volatility Filter
min_atr = input.float(0.5, "Min ATR % for Trading", step=0.1) / 100
use_volatility_filter = input.bool(true, "Enable Volatility Filter")
// Unbroken Range Visualization
show_unbroken_range = input.bool(true, "Show Unbroken Range Visualization", group="Unbroken Range")
unbroken_length = input.int(20, 'Minimum Range Length', minval=2, group="Unbroken Range")
unbroken_mult = input.float(1., 'Range Width', minval=0, step=0.1, group="Unbroken Range")
unbroken_atrLen = input.int(500, 'ATR Length', minval=1, group="Unbroken Range")
upCss = input.color(#089981, 'Broken Upward', group="Unbroken Range")
dnCss = input.color(#f23645, 'Broken Downward', group="Unbroken Range")
unbrokenCss = input.color(#2157f3, 'Unbroken', group="Unbroken Range")
// Calculate Indicators
ema_high = ta.ema(high, ema_length)
ema_low = ta.ema(low, ema_length)
ema_200 = ta.ema(close, 200)
sar = ta.sar(sar_start, sar_increment, sar_max)
atr = ta.atr(atr_length)
// Volatility Filter
atr_percentage = (atr / close) * 100
sufficient_volatility = not use_volatility_filter or (atr_percentage >= min_atr * 100)
// Range Detection Logic
range_high = ta.highest(high, range_length)
range_low = ta.lowest(low, range_length)
range_size = range_high - range_low
range_percentage = (range_size / close) * 100
range_condition = range_percentage <= (range_threshold * 100)
// Consecutive range bars counter
var int range_bars_count = 0
range_bars_count := range_condition ? range_bars_count + 1 : 0
in_range = range_bars_count >= min_range_bars
// Visualize Range
bgcolor(in_range ? color.new(color.purple, 85) : na, title="Range Zone")
plot(in_range ? range_high : na, "Range High", color=color.purple, linewidth=2, style=plot.style_linebr)
plot(in_range ? range_low : na, "Range Low", color=color.purple, linewidth=2, style=plot.style_linebr)
// Trading Conditions
buy_condition = (close > ema_high) and (close > ema_low)
sell_condition = (close < ema_high) and (close < ema_low)
// Apply Filters
buy_signal = buy_condition and not in_range and sufficient_volatility
sell_signal = sell_condition and not in_range and sufficient_volatility
// Trend State Machine
var int currentTrend = 0 // 0=neutral, 1=long, -1=short
var bool showBuy = false
var bool showSell = false
trendChanged = false
if buy_signal and currentTrend != 1
currentTrend := 1
trendChanged := true
showBuy := true
showSell := false
else if sell_signal and currentTrend != -1
currentTrend := -1
trendChanged := true
showBuy := false
showSell := true
// Reset signals
showBuy := nz(showBuy[1]) and not trendChanged ? false : showBuy
showSell := nz(showSell[1]) and not trendChanged ? false : showSell
// Plot Indicators
plot(ema_high, "EMA High", color=color.blue, linewidth=2)
plot(ema_low, "EMA Low", color=color.orange, linewidth=2)
plot(ema_200, "200 EMA", color=color.white, linewidth=3)
// Plot SAR with color coding
color sarColor = strategy.position_size > 0 ? color.red :
strategy.position_size < 0 ? color.green :
color.gray
plot(use_parabolic_sar ? sar : na, "SAR", style=plot.style_circles, color=sarColor, linewidth=2)
// Plot signals
plotshape(showBuy, title="Buy Signal", text="BUY", style=shape.labelup,
location=location.belowbar, color=color.green, textcolor=color.white, size=size.tiny)
plotshape(showSell, title="Sell Signal", text="SELL", style=shape.labeldown,
location=location.abovebar, color=color.red, textcolor=color.white, size=size.tiny)
// Strategy Logic
var float long_sl = na
var float long_tp = na
var float short_sl = na
var float short_tp = na
var float long_trail_stop = na
var float short_trail_stop = na
// Position Sizing with Volatility Scaling
position_size = use_volatility_filter ? math.min(100, 100 * (min_atr * 100) / atr_percentage) : 100
// Execute trades
if (showBuy)
// Calculate stop loss and take profit
long_sl := use_atr_stop ? close - atr * atr_mult : ema_low * (1 - sl_offset)
long_tp := close * (1 + tp_percent)
long_trail_stop := use_parabolic_sar ? sar : na
strategy.close("Short", comment="Exit Short")
strategy.entry("Long", strategy.long, qty=position_size)
// Set exit orders with toggle support
if use_stop_loss or use_take_profit
strategy.exit("Long SL/TP", "Long",
stop=use_stop_loss ? long_sl : na,
limit=use_take_profit ? long_tp : na)
if use_parabolic_sar
strategy.exit("Long SAR", "Long", stop=long_trail_stop)
alert("BUY: " + syminfo.ticker, alert.freq_once_per_bar)
if (showSell)
// Calculate stop loss and take profit
short_sl := use_atr_stop ? close + atr * atr_mult : ema_high * (1 + sl_offset)
short_tp := close * (1 - tp_percent)
short_trail_stop := use_parabolic_sar ? sar : na
strategy.close("Long", comment="Exit Long")
strategy.entry("Short", strategy.short, qty=position_size)
// Set exit orders with toggle support
if use_stop_loss or use_take_profit
strategy.exit("Short SL/TP", "Short",
stop=use_stop_loss ? short_sl : na,
limit=use_take_profit ? short_tp : na)
if use_parabolic_sar
strategy.exit("Short SAR", "Short", stop=short_trail_stop)
alert("SELL: " + syminfo.ticker, alert.freq_once_per_bar)
// Update SAR trailing stops
if barstate.isrealtime and use_parabolic_sar
if strategy.position_size > 0
strategy.exit("Long SAR", "Long", stop=long_trail_stop)
else if strategy.position_size < 0
strategy.exit("Short SAR", "Short", stop=short_trail_stop)
// Plot SL/TP levels with toggle support
plot(strategy.position_size > 0 and use_stop_loss ? long_sl : na, "Long Stop", color=color.red, style=plot.style_linebr, linewidth=2)
plot(strategy.position_size > 0 and use_take_profit ? long_tp : na, "Long Take Profit", color=color.green, style=plot.style_linebr, linewidth=2)
plot(strategy.position_size < 0 and use_stop_loss ? short_sl : na, "Short Stop", color=color.red, style=plot.style_linebr, linewidth=2)
plot(strategy.position_size < 0 and use_take_profit ? short_tp : na, "Short Take Profit", color=color.green, style=plot.style_linebr, linewidth=2)
// Plot SAR trailing stops
plot(strategy.position_size > 0 and use_parabolic_sar ? long_trail_stop : na,
"Long Trail Stop", color=color.orange, style=plot.style_circles, linewidth=2)
plot(strategy.position_size < 0 and use_parabolic_sar ? short_trail_stop : na,
"Short Trail Stop", color=color.orange, style=plot.style_circles, linewidth=2)
// Alerts
alertcondition(showBuy, title="Buy Alert", message="BUY: {{ticker}}")
alertcondition(showSell, title="Sell Alert", message="SELL: {{ticker}}")