
This strategy is a quantitative trading system based on multi-timeframe analysis, primarily utilizing MACD indicator, RSI indicator, VWAP moving averages, and ATR volatility filter to execute trades based on comprehensive signals from 30-minute and 1-hour timeframes. The strategy supports both long and short positions, confirming signals through technical indicator crossovers across different timeframes, combined with volatility conditions filtering to improve trade quality. The strategy incorporates fixed percentage take-profit and stop-loss mechanisms, while also exiting trades when technical indicators reverse, aiming to capture medium to short-term price movements.
The core principle of this strategy is to filter low-quality signals through multiple confirmation conditions, primarily including the following key components:
Multi-Timeframe MACD Crossover Signals:
RSI Overbought/Oversold Filtering:
Dual VWAP Price Position Confirmation:
Volatility Filter:
Multi-Level Exit Mechanism:
Through this multi-level condition filtering and confirmation, the strategy aims to capture medium to short-term movements with clear directionality, while filtering out low-quality signals to improve win rate and risk-reward ratio.
Multi-Timeframe Confirmation: By combining signals from 30-minute and 1-hour timeframes, the strategy can better identify genuine trends and reduce the impact of false signals. The 1-hour MACD trend confirmation feature is particularly helpful in avoiding counter-trend trading.
Volatility Adaptability: The ATR volatility filter ensures that the strategy only enters trades when the market has sufficient momentum, avoiding trading in low-volatility ranges, which effectively reduces the risk of sideways oscillation.
Flexible Exit Mechanism: The strategy includes not only fixed take-profit and stop-loss levels but also dynamic exit mechanisms based on indicator reversals, allowing for timely exits when the market begins to reverse before reaching the take-profit level, thereby protecting profits.
Dual Price Position Confirmation: Requiring price to be simultaneously above (for longs) or below (for shorts) VWAPs in two timeframes further confirms price momentum and direction, reducing false breakouts.
Built-in Risk Management: The strategy incorporates stop-loss mechanisms and position sizing (default 5% of account equity per trade), which helps control risk exposure for each trade and protects capital.
Low Win Rate Challenge: As noted in the code comments, the strategy may face a low win rate issue. This is because while multiple condition filtering improves signal quality, it also significantly reduces trading frequency, resulting in a smaller sample size with limited statistical significance.
Parameter Sensitivity: The strategy uses multiple adjustable parameters, including MACD lengths, RSI thresholds, and ATR filter parameters. Small changes in these parameters may significantly impact strategy performance, posing a risk of over-optimization.
Limitations of Fixed Percentage Take-Profit/Stop-Loss: Using the same take-profit (1.5%) and stop-loss (0.5%) ratios for all market environments may not adapt to different volatility conditions. In high-volatility markets, the stop-loss may be too tight; in low-volatility markets, the take-profit may be too distant.
Multi-Timeframe Lag: Using signals from longer timeframes (such as 1-hour) as confirmation may introduce lag, causing missed entry opportunities or delayed exits.
Lack of Market Environment Adaptability: The strategy does not include mechanisms to differentiate between different market environments (trending/ranging), which may lead to poor performance under certain market conditions.
Solutions: - Consider introducing adaptive take-profit/stop-loss mechanisms based on ATR or other volatility indicators - Add market environment recognition modules to adjust strategy parameters or trading logic under different conditions - Implement more rigorous backtesting and forward testing validation to avoid over-optimization - Consider adding trading filters, such as time filters or trend strength filters, to further improve signal quality
Dynamic Take-Profit/Stop-Loss Optimization: Change the fixed percentage take-profit/stop-loss to dynamic values based on ATR, for example, using 1.5×ATR as stop-loss and 3×ATR as take-profit. This allows the strategy to better adapt to different market volatility environments, providing looser stops in high-volatility periods and tighter profit targets in low-volatility periods.
Market Environment Classification: Introduce market environment recognition mechanisms to distinguish between trending and ranging markets. ADX, Bollinger Band width, or the relationship between price and long-term moving averages can be used to identify market states and adjust strategy parameters or even completely switch trading logic accordingly.
Entry Timing Optimization: The current strategy enters on the current candle when a MACD crossover occurs, potentially facing slippage or execution delays. Consider entering at the open of the next candle after crossover confirmation, or setting limit orders to enter at specific price zones for better execution prices.
Time Filter: Add trading time filters to avoid specific inefficient trading sessions. For example, avoid trading during the end of the Asian session or the European-American transition periods when liquidity may be lower or volatility irregular.
Indicator Parameter Adaptability: Design MACD, RSI, and ATR parameters as adaptive values that automatically adjust based on recent market volatility or cyclicality. For example, use shorter MACD parameters in high-volatility markets and longer parameters in low-volatility markets.
Signal Strength Grading: Establish a strength scoring system for entry signals based on multiple factors (such as MACD histogram size, RSI deviation, VWAP distance, etc.) to score signals, only executing trades with strength exceeding specific thresholds, or dynamically adjusting position size based on signal strength.
Machine Learning Enhancement: Introduce machine learning models to predict which signals are more likely to generate profitable trades, training models to identify the most valuable pattern combinations based on historical data. This can improve the strategy’s adaptability and win rate.
These optimization directions aim to enhance the strategy’s robustness, adaptability, and long-term performance while maintaining its core logic. Through these improvements, the strategy can better respond to changes in different market environments and conditions.
The Multi-Timeframe MACD-RSI Crossover Volatility-Filtered Quantitative Trading Strategy is a comprehensively designed trading system that identifies high-quality trading opportunities by combining multiple technical indicators and signals from multiple timeframes. The core advantages of this strategy lie in its multi-level signal confirmation mechanism and built-in risk management features, enabling it to capture price movements while controlling risk.
Despite the challenge of a relatively low win rate, the strategy maintains a positive expected value by increasing the average profit of winning trades. By implementing the suggested optimization measures, particularly dynamic take-profit/stop-loss, market environment classification, and signal strength grading, strategy performance can be further enhanced.
This strategy is suitable for medium to short-term traders, especially those seeking a systematic trading approach based on technical analysis who value risk management. The strategy’s multi-condition confirmation mechanism, while reducing trading frequency, improves the quality of each trade, aligning with the trading philosophy that “less is more,” emphasizing quality over quantity.
In practical application, it is recommended that traders first test this strategy in a simulated environment, particularly testing the effects of various optimization measures, before cautiously applying it to live trading. Meanwhile, continuously monitoring changes in market conditions and adjusting strategy parameters accordingly will help maintain stable long-term performance.
/*backtest
start: 2025-01-01 00:00:00
end: 2025-06-08 00:00:00
period: 1h
basePeriod: 1h
exchanges: [{"eid":"Futures_Binance","currency":"ETH_USDT"}]
*/
// This Pine Script® code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © GentlemanOfTrading
//@version=6
strategy(title = "ETH Day Trader", overlay = true, margin_long = 100, margin_short = 100, default_qty_type = strategy.percent_of_equity, default_qty_value = 5)
// ==== 1) USER INPUTS ====
// MACD/RSI lengths
fastLen = input.int(12, title="MACD Fast EMA Length", minval=1)
slowLen = input.int(26, title="MACD Slow EMA Length", minval=1)
signalLen = input.int(9, title="MACD Signal EMA Length", minval=1)
rsiLen = input.int(14, title="RSI Length", minval=1)
// RSI thresholds
rsiThreshLong30 = input.int(55, title="RSI30m > (Long)", minval=1, maxval=100)
rsiThreshShort30= input.int(45, title="RSI30m < (Short)", minval=1, maxval=100)
rsiThresh1h = input.int(50, title="RSI1h Threshold", minval=1, maxval=100)
// ATR filter (30m)
atrLen = input.int(14, title="ATR Length (30m)", minval=1)
atrMaLen = input.int(20, title="ATR MA Length (30m)", minval=1)
// Take Profit / Stop Loss (percent)
tpPerc = input.float(1.5, title="Take Profit (%)", minval=0.1) / 100
slPerc = input.float(0.5, title="Stop Loss (%)", minval=0.1) / 100
// Toggle whether to use 1h trend confirmation
use1hTrend = input.bool(true, title="Use 1h MACD Trend Confirmation?")
// ==== 2) FETCH INDICATORS ON 30m ====
// We assume this script is applied on a chart ≤ 30m (e.g. 15m or 5m),
// but if you apply it on a 30m chart it still works: security() with "30" just returns the same bar.
[macd30m, macdSig30m, _] = ta.macd(close, fastLen, slowLen, signalLen)
rsi30m = ta.rsi(close, rsiLen)
atr30m = ta.atr(atrLen)
// ==== 3) FETCH INDICATORS ON 1h & VWAPs via request.security() ====
// --- 1h MACD & RSI ---
[macd1h, macdSig1h, _] = request.security(syminfo.tickerid, "60", ta.macd(close, fastLen, slowLen, signalLen), lookahead=barmerge.lookahead_off)
rsi1h = request.security(syminfo.tickerid, "60", ta.rsi(close, rsiLen), lookahead=barmerge.lookahead_off)
// --- 30m VWAP & 1h VWAP (session VWAP) ---
vwap30m = request.security(syminfo.tickerid, "30", ta.vwap(close), lookahead=barmerge.lookahead_off)
vwap1h = request.security(syminfo.tickerid, "60", ta.vwap(close), lookahead=barmerge.lookahead_off)
// ==== 4) BUILD VOLATILITY FILTER (30m ATR vs ATR MA) ====
atr30m_ma = ta.sma(atr30m, atrMaLen)
volatilityOK = atr30m >= atr30m_ma
// ==== 5) MULTI-TIMEFRAME CROSS CONDITIONS ====
// 30m MACD cross signals
longCross30m = ta.crossover(macd30m, macdSig30m)
shortCross30m = ta.crossunder(macd30m, macdSig30m)
// 1h MACD trend confirmation
macdTrendUp1h = macd1h > macdSig1h
macdTrendDown1h = macd1h < macdSig1h
// ==== 6) ENTRY & EXIT CONDITIONS ====
// LONG ENTRY:
// • 30m MACD crossover
// • 30m RSI > rsiThreshLong30
// • (optionally) 1h MACD line > 1h MACD signal
// • Price > 30m VWAP AND Price > 1h VWAP
// • 30m ATR ≥ 30m ATR MA (volatility filter)
longEntryCond =
longCross30m
and (rsi30m > rsiThreshLong30)
and (close > vwap30m)
and (close > vwap1h)
and volatilityOK
and (use1hTrend ? macdTrendUp1h : true)
// LONG EXIT:
// • fixed TP/SL
// OR • 30m MACD crossunder
// OR • 1h MACD falls below signal (trend flipped)
var float entryPriceLong = na
longExitCond = false
if (strategy.position_size > 0)
// Price-based TP / SL checks
entryPriceLong := nz(entryPriceLong[1], strategy.position_avg_price)
longTPprice = entryPriceLong * (1 + tpPerc)
longSLprice = entryPriceLong * (1 - slPerc)
// check TP/SL first
longExitTP = high >= longTPprice
longExitSL = low <= longSLprice
// fallback: MACD crossunder on 30m OR 1h trend flips
macdTrendFlip1h = macdTrendUp1h and (macd1h < macdSig1h)
macdCross30m = shortCross30m
longExitCond := longExitTP or longExitSL or macdCross30m or macdTrendFlip1h
else
entryPriceLong := na // reset when no position
// SHORT ENTRY:
// • 30m MACD crossunder
// • 30m RSI < rsiThreshShort30
// • (optionally) 1h MACD line < 1h MACD signal
// • Price < 30m VWAP AND Price < 1h VWAP
// • 30m ATR ≥ 30m ATR MA (volatility filter)
shortEntryCond =
shortCross30m
and (rsi30m < rsiThreshShort30)
and (close < vwap30m)
and (close < vwap1h)
and volatilityOK
and (use1hTrend ? macdTrendDown1h : true)
// SHORT EXIT:
// • fixed TP/SL
// OR • 30m MACD crossover
// OR • 1h MACD flips up
var float entryPriceShort = na
shortExitCond = false
if (strategy.position_size < 0)
entryPriceShort := nz(entryPriceShort[1], strategy.position_avg_price)
shortTPprice = entryPriceShort * (1 - tpPerc)
shortSLprice = entryPriceShort * (1 + slPerc)
// check TP/SL first
shortExitTP = low <= shortTPprice
shortExitSL = high >= shortSLprice
macdTrendFlipUp1h = macdTrendDown1h and (macd1h > macdSig1h)
macdCrossUp30m = longCross30m
shortExitCond := shortExitTP or shortExitSL or macdCrossUp30m or macdTrendFlipUp1h
else
entryPriceShort := na // reset when no position
// ==== 7) EXECUTE STRATEGY ORDERS WITH LABELS & ALERTS ====
// — Long Entry —
if (longEntryCond and strategy.position_size == 0)
strategy.entry("Long", strategy.long)
label.new(bar_index, low, text="Buy (LT)", style=label.style_label_up, color=color.new(color.green, 0), textcolor=color.white, yloc=yloc.belowbar)
alert("Buy (LT)", alert.freq_once_per_bar_close)
// — Long Exit —
if (strategy.position_size > 0 and longExitCond)
strategy.close("Long")
label.new(bar_index, high, text="Sell (LT)", style=label.style_label_down, color=color.new(color.red, 0), textcolor=color.white, yloc=yloc.abovebar)
alert("Sell (LT)", alert.freq_once_per_bar_close)
// — Short Entry —
if (shortEntryCond and strategy.position_size == 0)
strategy.entry("Short", strategy.short)
label.new(bar_index, high, text="Sell (ST)", style=label.style_label_down, color=color.new(color.red, 0), textcolor=color.white, yloc=yloc.abovebar)
alert("Sell (ST)", alert.freq_once_per_bar_close)
// — Short Exit —
if (strategy.position_size < 0 and shortExitCond)
strategy.close("Short")
label.new(bar_index, low, text="Buy (ST)", style=label.style_label_up, color=color.new(color.green, 0), textcolor=color.white, yloc=yloc.belowbar)
alert("Buy (ST)", alert.freq_once_per_bar_close)
// ==== 8) OPTIONAL PLOTTING (for debugging) ====
// We’ve removed any `transp`/`opacity` arguments. Instead, we use `color.new(baseColor, α)`
// for transparency, where α = 0 is fully opaque and α = 255 is fully transparent.
// 30m VWAP
plot(vwap30m, title = "VWAP 30m", color = color.new(color.teal, 80)) // ~31% transparentlinewidth= 1
// 1h VWAP
plot(vwap1h, title = "VWAP 1h", color = color.new(color.fuchsia, 80), linewidth= 1) // ~31% transparent
// 30m ATR vs ATR_MA
plot(atr30m, title = "ATR 30m", color = color.new(color.orange, 80))
plot(atr30m_ma, title = "ATR30m MA", color = color.new(color.yellow, 80))
// 30m MACD Histogram (bars)
plot(macd30m - macdSig30m, title = "MACD Histogram (30m)", style = plot.style_columns, color = (macd30m - macdSig30m >= 0 ? color.new(color.green, 80) : color.new(color.red, 80)))
// 1h MACD Histogram (area)
h1 = request.security(syminfo.tickerid, "60", macd1h - macdSig1h, lookahead=barmerge.lookahead_off)
plot(1, title = "MACD Hist (1h)", style = plot.style_area, color = (h1 >= 0 ? color.new(color.green, 80) : color.new(color.red, 80)))