
The Dual Moving Average Momentum Trend Following and Reversal Trading System is a comprehensive quantitative trading strategy that combines elements of trend following and reversal trading. The strategy utilizes two moving averages of different periods (100 and 500) to determine market trend direction, while integrating multiple technical indicators as filters, including RSI (Relative Strength Index), ADX (Average Directional Index), and ATR (Average True Range). The system allows for bi-directional trading, enabling both long and short positions with different entry and exit rules based on varying market conditions. This strategy is particularly suitable for volatile markets such as cryptocurrencies, capable of following strong uptrends while also seeking short opportunities under extremely oversold conditions.
The core principle of the strategy is based on a dual verification mechanism of trend identification and momentum confirmation:
Trend Identification: The strategy uses 100-period and 500-period moving averages (either EMA or SMA) to determine market trends. When MA100 is above MA500, it is considered an uptrend; otherwise, it might be a downtrend.
Long Entry Conditions:
Short Entry Conditions:
Risk Management and Exit Strategy:
This design enables the strategy to capture major moves in trending markets while also seeking reversal points in oversold conditions.
High Adaptability: The strategy offers high customization capabilities through multiple optional filters (RSI, ADX, ATR), allowing adaptation to different market environments and trader styles. Users can flexibly enable or disable these filters based on current market conditions.
Bi-Directional Trading: Unlike purely trend following or reversal systems, this strategy combines both trading methods, enabling long positions in uptrends and short positions in extreme oversold conditions, increasing profit opportunities.
Intelligent Trend Determination: Using a dual moving average system (MA100 and MA500) provides more reliable trend judgment, filtering false breakouts better than single moving average systems.
Dynamic Volatility Adaptation: Through the ATR filter, the strategy can automatically adapt to changes in market volatility, avoiding frequent trading in low volatility environments and reducing unnecessary trading costs.
Prevention of Chasing Strong Reversals: The short trading setup includes a “strong uptrend block” mechanism, prohibiting shorts when MA100 is above MA500 by more than a set percentage, effectively avoiding the risk of trading against strong upward trends.
Multiple Confirmation Mechanism: Entry signals require confirmation from multiple technical indicators, significantly reducing the probability of false signals and enhancing the strategy’s robustness.
Flexible Exit Mechanism: The strategy designs different exit logic for longs and shorts, with longs able to use MA500 as a dynamic stop-loss, while shorts have a fixed profit target, aligning with the characteristics of different directional trades.
Parameter Sensitivity: The strategy relies on multiple technical indicators and parameter settings, and small changes in these parameters may lead to significant differences in backtesting results. In actual trading, optimal parameters may change with market conditions, creating a risk of overfitting historical data. The solution is to use step optimization and forward testing to validate parameter robustness.
Lag Risk: Indicators such as moving averages are inherently lagging, and may not capture turning points in time during volatile markets, leading to delayed entries or exits. It is recommended to appropriately shorten moving average periods or add other leading indicators in highly volatile markets.
Poor Performance During Trend Transitions: In oscillating markets or during trend transitions, the strategy may generate frequent false signals, resulting in consecutive losses. The solution is to add a market state identification mechanism, automatically reducing position size or pausing trading when oscillating markets are identified.
Money Management Risk: The strategy uses 100% of account funds by default, plus allowing one pyramid addition, which may face significant drawdowns in adverse market conditions. It is recommended to adjust position size based on personal risk tolerance and avoid using all funds for trading.
Liquidity Risk: In markets or periods with lower liquidity, there may be increased slippage or inability to execute at expected prices. It is recommended to run the strategy on mainstream trading pairs and during periods of adequate liquidity.
Black Swan Event Risk: Fixed percentage stop-losses may not be effectively executed under extreme market conditions, especially in cases of price gaps. It is recommended to set maximum loss limits and consider using derivatives such as options to hedge extreme risks.
Introduction of Market State Classification: The current strategy uses the same parameter settings across different market states (trending, oscillating, high volatility, low volatility). Consider adding market state identification functionality and optimizing different parameter sets for different states. This can be implemented by using volatility indicators (such as ATR percentage) or trend strength indicators (such as ADX thresholds) to distinguish market states.
Optimize Money Management: The strategy currently uses a fixed proportion of account funds. This could be improved to volatility-based dynamic position sizing, increasing positions in low volatility environments and reducing them in high volatility environments to achieve risk balance. Relative values of ATR can be used to dynamically adjust the proportion of funds for each trade.
Add Time Filters: Some markets may perform better or worse during specific time periods. Adding time filtering functionality can help avoid historically underperforming time periods. This can be implemented by analyzing strategy performance across different time periods (such as Asian, European, and American trading sessions).
Multi-Timeframe Confirmation: The current strategy operates on a single timeframe (3-hour). Consider adding higher timeframe trend confirmation, entering only when the higher timeframe trend direction is consistent, improving win rates. For example, execute long signals on the 3-hour chart only when the daily chart trend is upward.
Dynamic Stop-Loss and Take-Profit: Replace fixed percentages with dynamic, market volatility-based stop-loss and take-profit levels, allowing the strategy to better adapt to different volatility environments. ATR multiples can be used to set stop-loss and take-profit levels, automatically widening stop-loss ranges as volatility increases.
Integrate Sentiment Indicators: Add market sentiment indicators as additional filters, such as trading volume, funding rates (for perpetual contracts), or futures premiums, to avoid trading against the trend in extreme sentiment states. These indicators can serve as warning signals for overheated or overcooled markets.
Machine Learning Optimization: Use machine learning algorithms to dynamically select optimal parameter combinations, automatically adjusting strategy parameters based on recent market conditions. This can be implemented through rolling window parameter optimization or reinforcement learning methods.
The Dual Moving Average Momentum Trend Following and Reversal Trading System is an ingeniously designed quantitative trading strategy that combines moving average systems, momentum indicators, and volatility filters to provide high adaptability and customization capabilities while maintaining strategy simplicity. The core advantages of this strategy lie in its multiple confirmation mechanisms and flexible filtering system, enabling it to adapt to different market environments.
However, like all trading strategies, it also faces challenges such as parameter sensitivity, lag, and changing market conditions. Through improvements in market state classification, dynamic money management, multi-timeframe analysis, and machine learning optimization, the strategy’s robustness and adaptability can be further enhanced.
Most importantly, traders applying this strategy should fully understand its principles and limitations, make appropriate adjustments based on personal risk preferences and market experience, and always adhere to strict risk management principles. There is no perfect trading strategy, but through continuous optimization and careful application, this system can become a powerful weapon in a trader’s toolkit.
/*backtest
start: 2024-05-26 00:00:00
end: 2025-05-25 00:00:00
period: 1h
basePeriod: 1h
exchanges: [{"eid":"Futures_Binance","currency":"SOL_USDT"}]
*/
//@version=6
strategy("Momentum Long + Short Strategy (BTC 3H)", overlay=true,
default_qty_type=strategy.percent_of_equity,
default_qty_value=100,
initial_capital=1000,
commission_type=strategy.commission.percent,
commission_value=0.1,
slippage=1,
pyramiding=1)
// ==============================================================================
// === LONG TRADE SETTINGS
// ==============================================================================
enableLongs = input.bool(true, "Enable Long Trades", group="LONG TRADE SETTINGS")
slPercentLong = input.float(3.0, "Long Stop Loss %", minval=0.1, group="LONG TRADE SETTINGS")
useRSIFilter = input.bool(false, "Enable RSI Filter", group="LONG FILTER SETTINGS")
useADXFilter = input.bool(false, "Enable ADX Filter", group="LONG FILTER SETTINGS")
useATRFilter = input.bool(false, "Enable ATR Filter", group="LONG FILTER SETTINGS")
useTrendFilter = input.bool(true, "Require MA100 > MA500", group="LONG FILTER SETTINGS")
smoothType = input.string("EMA", "Smoothing Type", options=["EMA", "SMA"], group="LONG FILTER SETTINGS")
smoothingLength = input.int(100, "Smoothing Length (for filters)", group="LONG FILTER SETTINGS")
rsiLengthLong = input.int(14, "RSI Length", group="RSI FILTER")
adxLength = input.int(14, "ADX Length", group="ADX FILTER")
atrLength = input.int(14, "ATR Length", group="ATR FILTER")
// ==============================================================================
// === SHORT TRADE SETTINGS
// ==============================================================================
enableShorts = input.bool(false, "Enable Short Trades", group="SHORT TRADE SETTINGS")
slPercentShort = input.float(3.0, "Short Stop Loss %", minval=0.1, group="SHORT TRADE SETTINGS")
tpPercentShort = input.float(4.0, "Short Take Profit %", minval=0.1, group="SHORT TRADE SETTINGS")
rsiLengthShort = input.int(14, "RSI Length", group="SHORT FILTER SETTINGS")
rsiThresholdShort = input.float(33, "RSI Threshold", minval=1, maxval=100, group="SHORT FILTER SETTINGS")
bbLength = input.int(20, "Bollinger Band Length", group="SHORT FILTER SETTINGS")
useATRFilterShort = input.bool(true, "Enable ATR Filter (Short)", group="SHORT FILTER SETTINGS")
useStrongUptrendBlock = input.bool(true, "Block Shorts if MA100 > MA500 by (%)", group="SHORT FILTER SETTINGS")
shortTrendGapPct = input.float(2.0, "Threshold (%) for Blocking Shorts", minval=0.1, group="SHORT FILTER SETTINGS")
// ==============================================================================
// === COMMON INDICATORS
// ==============================================================================
ma100 = smoothType == "EMA" ? ta.ema(close, 100) : ta.sma(close, 100)
ma500 = smoothType == "EMA" ? ta.ema(close, 500) : ta.sma(close, 500)
priceAboveMAs = close > ma100 and close > ma500
trendAlignment = not useTrendFilter or ma100 > ma500
plot(ma100, title="MA 100", color=color.orange)
plot(ma500, title="MA 500", color=color.blue)
// ==============================================================================
// === LONG FILTER LOGIC
// ==============================================================================
rsiLong = ta.rsi(close, rsiLengthLong)
rsiSmooth = smoothType == "EMA" ? ta.ema(rsiLong, smoothingLength) : ta.sma(rsiLong, smoothingLength)
rsiPass = not useRSIFilter or rsiLong > rsiSmooth
dmi(len) =>
up = ta.change(high)
down = -ta.change(low)
plusDM = na(up) ? na : (up > down and up > 0 ? up : 0)
minusDM = na(down) ? na : (down > up and down > 0 ? down : 0)
trur = ta.rma(ta.tr, len)
plusDI = 100 * ta.rma(plusDM, len) / trur
minusDI = 100 * ta.rma(minusDM, len) / trur
dx = 100 * math.abs(plusDI - minusDI) / (plusDI + minusDI)
ta.rma(dx, len)
adx = dmi(adxLength)
adxSmooth = smoothType == "EMA" ? ta.ema(adx, smoothingLength) : ta.sma(adx, smoothingLength)
adxPass = not useADXFilter or adx > adxSmooth
atr = ta.atr(atrLength)
atrSmooth = smoothType == "EMA" ? ta.ema(atr, smoothingLength) : ta.sma(atr, smoothingLength)
atrPass = not useATRFilter or atr > atrSmooth
// ==============================================================================
// === SHORT FILTER LOGIC
// ==============================================================================
rsiShort = ta.rsi(close, rsiLengthShort)
bbBasis = ta.sma(close, bbLength)
bbDev = ta.stdev(close, bbLength)
bbLower = bbBasis - bbDev * 2
priceBelowBB = close < bbLower
priceBelowMAs = close < ma100 and close < ma500
rsiOversold = rsiShort < rsiThresholdShort
atrShort = ta.atr(atrLength)
atrShortSmoothed = smoothType == "EMA" ? ta.ema(atrShort, smoothingLength) : ta.sma(atrShort, smoothingLength)
atrShortPass = not useATRFilterShort or atrShort > atrShortSmoothed
emaGapTooWide = (ma100 - ma500) / ma500 * 100 > shortTrendGapPct
strongUptrendBlock = not useStrongUptrendBlock or not emaGapTooWide
// ==============================================================================
// === ENTRY CONDITIONS
// ==============================================================================
longCondition = enableLongs and priceAboveMAs and trendAlignment and rsiPass and adxPass and atrPass
shortCondition = enableShorts and priceBelowMAs and priceBelowBB and rsiOversold and atrShortPass and strongUptrendBlock
if longCondition
strategy.entry("Long", strategy.long)
if shortCondition
strategy.entry("Short", strategy.short)
// ==============================================================================
// === EXIT CONDITIONS
// ==============================================================================
longStop = strategy.position_avg_price * (1 - slPercentLong / 100)
strategy.exit("SL Long", from_entry="Long", stop=longStop)
if strategy.position_size > 0 and close < ma500
strategy.close("Long", comment="TP Below MA500")
shortStop = strategy.position_avg_price * (1 + slPercentShort / 100)
shortTP = strategy.position_avg_price * (1 - tpPercentShort / 100)
strategy.exit("SL/TP Short", from_entry="Short", stop=shortStop, limit=shortTP)