Multi-Timeframe Stochastic RSI Crossover Strategy

RSI STOCH RSI MTF TRENDING MARKETS STOP LOSS MULTI-STAGE TP
Created on: 2025-06-05 13:13:32 Modified on: 2025-06-05 13:13:32
Copy: 0 Number of hits: 336
avatar of ianzeng123 ianzeng123
2
Follow
319
Followers

 Multi-Timeframe Stochastic RSI Crossover Strategy  Multi-Timeframe Stochastic RSI Crossover Strategy

Strategy Overview

The Multi-Timeframe Stochastic RSI Crossover Strategy is a sophisticated trading system based on the Stochastic Relative Strength Index (Stoch RSI) indicator, employing data from both 5-minute and 15-minute timeframes for signal generation and confirmation. This comprehensive trading system includes clearly defined entry conditions, stop-loss controls, and a multi-stage profit-taking approach. The strategy specifically focuses on overbought/oversold market conditions, aiming to profit by capturing momentum shifts in price action.

The strategy operates on a 5-minute chart while referencing 15-minute data for confirmation, demonstrating a depth of multi-timeframe analysis. It employs different parameter settings for long and short trades, suggesting its design is tailored to perform in generally bullish market environments.

Strategy Principles

The core mechanism of this strategy is based on Stochastic RSI crossover signals, combined with a multi-timeframe confirmation process to filter out low-quality signals. The detailed workflow is as follows:

  1. Initial Trigger Signal (5-minute timeframe):

    • Long Signal: When the Stoch RSI K-line crosses above its D-line on the 5-minute chart, AND the K-value at the time of crossing is below a specified trigger level (stoch_5min_k_long_trigger).
    • Short Signal: When the Stoch RSI K-line crosses below its D-line on the 5-minute chart, AND the K-value at the time of crossing is above a specified trigger level (stoch_5min_k_short_trigger).
  2. Higher Timeframe Confirmation (15-minute timeframe):

    • After a 5-minute initial signal is triggered, the strategy waits for confirmation from the 15-minute timeframe within a configurable waiting window (wait_window_5min_bars).
    • Long Confirmation: The 15-minute Stoch RSI K-line must be strictly greater than its D-line, AND the 15-minute K-value must be below the stoch_15min_long_entry_level setting.
    • Short Confirmation: The 15-minute Stoch RSI K-line must be strictly less than its D-line, AND the 15-minute K-value must be above the stoch_15min_short_entry_level setting.
  3. Duplicate Signal Filtering:

    • The strategy implements a cooldown mechanism (min_bars_between_signals) that requires a specified number of bars to pass before another signal in the same direction can be considered.
  4. Position Management:

    • Position Lock: No new entry signals are generated when an open position is already held.
    • Stop-Loss Setting: Based on the low (for longs) or high (for shorts) of the entry bar, triggered if a subsequent 5-minute bar’s closing price breaches this level.
    • Stop-loss checks are prioritized over take-profit checks.
  5. Two-Stage Profit-Taking Mechanism:

    • First Stage (TP1): Closes 50% of the position, triggered by either:
      • Priority A (Extreme K Levels): If the 5-minute OR 15-minute Stoch K value exceeds extreme_long_tp_level (for longs) or drops below extreme_short_tp_level (for shorts).
      • Priority B (Conditional 5-min Cross + 15-min K-Reversal): When a 5-minute Stoch RSI K/D crossover occurs (K crosses below D for longs; K crosses above D for shorts) AND this is confirmed by a 15-minute Stoch K-value “reversal” (current 15m K < previous 15m K for longs; current 15m K > previous 15m K for shorts).
    • Second Stage (TP2): Closes the remaining 50% of the position, only active after TP1 has been triggered, when the 5-minute or 15-minute Stoch K value reaches the same extreme level again.

Strategy Advantages

  1. Multi-Timeframe Confirmation Mechanism:

    • By combining signals from 5-minute and 15-minute timeframes, the strategy reduces false signals and improves trade quality. The shorter timeframe provides entry opportunities while the longer timeframe offers trend confirmation, effectively filtering out short-term market noise.
  2. Precise Identification of Overbought/Oversold Conditions:

    • Stochastic RSI is more sensitive than traditional RSI, enabling earlier detection of changes in price momentum. The strategy leverages this feature to trade at potential reversal points, improving entry timing accuracy.
  3. Staged Profit-Taking Approach:

    • The two-stage profit-taking mechanism allows traders to secure partial profits while maintaining remaining positions to capture larger moves. This approach balances risk and reward, particularly suitable for volatile markets.
  4. Adaptive Bias Settings:

    • Strategy parameters can be adjusted to reflect market bias (such as the current strategy’s more lenient approach to long positions), making it adaptable to different market environments and trading preferences.
  5. Comprehensive Risk Management:

    • Clear stop-loss mechanisms based on entry bar extremes provide quantifiable risk control for each trade. Stop-loss checks take priority over profit-taking checks, ensuring risk control remains the primary consideration.
  6. Duplicate Signal Filtering:

    • By implementing a signal cooldown period, the strategy avoids overtrading in the same direction within short periods, reducing transaction costs and improving signal quality.

Strategy Risks

  1. Parameter Sensitivity:

    • The strategy relies on multiple threshold parameters, such as various trigger levels and confirmation thresholds. Inappropriate parameter settings may result in excessive false signals or missed key trading opportunities. These parameters should be optimized through backtesting across different market conditions.
  2. Potentially Wide Stop-Loss Points:

    • Stop-losses based on entry bar extremes may be relatively loose in some situations, especially in volatile markets. This could lead to larger-than-expected losses on individual trades. Consider implementing maximum stop-loss limits to avoid excessive risk exposure.
  3. Market Condition Dependency:

    • Stochastic RSI performs well in range-bound markets but may generate premature reversal signals in strong trending markets. The strategy might underperform in rapid one-directional moves, as prices can remain in overbought or oversold territories without reversing.
  4. Long Bias:

    • The current strategy configuration shows a preference for long trades, which may lead to overtrading or inappropriate long signals in bearish market environments. Parameters should be adjusted according to market environment to maintain balance.
  5. 15-Minute Confirmation Delay:

    • Waiting for 15-minute timeframe confirmation may introduce entry delays, potentially missing optimal entry points in fast-moving markets. In extremely volatile markets, this delay could significantly impact strategy performance.

Strategy Optimization Directions

  1. Dynamic Profit-Taking Mechanism:

    • The current fixed percentage profit-taking can be upgraded to ATR-based dynamic take-profits or trailing stop implementation to capture more profit in trending markets. Particularly for the second stage of profit-taking, consider using volatility or trend strength adjusted take-profit levels.
  2. Market State Adaptation:

    • Introduce market state detection mechanisms (such as ADX for trend strength assessment) to automatically adjust parameters based on different market environments. For example, relaxing reversal conditions in strong trending markets and tightening these conditions in ranging markets.
  3. Multi-Indicator Synergy Confirmation:

    • Integrate additional technical indicators such as MACD, Bollinger Bands, or moving averages as supplementary confirmation tools. Multi-indicator confluence can improve signal reliability and reduce false breakouts.
  4. Risk Exposure Optimization:

    • Implement dynamic position sizing based on current volatility or recent trading performance. Additionally, add maximum stop-loss distance limits to prevent extreme losses.
  5. Multi-Timeframe Hierarchy Extension:

    • Consider adding a third timeframe (such as 1-hour or 4-hour) to provide higher-level market context analysis, particularly for trend confirmation and major support/resistance identification.
  6. Trading Session Filters:

    • Add trading session filters to avoid trading during periods of inadequate liquidity or irregular volatility. For example, restrict trading during highly volatile periods around market opening and closing.

Summary

The Multi-Timeframe Stochastic RSI Crossover Strategy is a well-structured trading system that enhances trade quality through multi-timeframe analysis and a rigorous signal confirmation process. The strategy’s core strengths lie in its comprehensive entry conditions and risk management system, particularly the two-stage profit-taking mechanism that allows for profit locking while maintaining partial positions to track trends.

However, the strategy’s effectiveness is highly dependent on parameter settings and market conditions. It may perform well in oscillating markets but might require adjustments in strong trending or highly volatile environments. Traders are advised to optimize parameters through historical backtesting and consider adding features such as market state detection and dynamic profit-taking mechanisms to enhance adaptability.

This strategy is best suited for intermediate to advanced traders with programming knowledge who can understand and customize these complex trading rules. With proper parameter adjustment and risk management, this system can be a valuable component in day and short-term traders’ toolkits, especially those focused on capturing market momentum shifts.

Strategy source code
/*backtest
start: 2024-06-05 00:00:00
end: 2025-06-04 00:00:00
period: 2d
basePeriod: 2d
exchanges: [{"eid":"Futures_Binance","currency":"DOGE_USDT"}]
*/

// This Pine Script® code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © Archertoria

//@version=6
strategy("System 0530 - Stoch RSI Strategy v13 SL-Priority TP-Reversal", 
         overlay=true, 
         default_qty_type=strategy.percent_of_equity, 
         default_qty_value=100, 
         calc_on_order_fills=false, 
         process_orders_on_close=true,
         margin_short=50) 

// --- Original Indicator Input Parameters ---
g_stoch = "Stochastic RSI Parameters"
rsi_len = input.int(14, "RSI Period", minval=1, group=g_stoch)
stoch_rsi_len = input.int(14, "Stochastic of RSI Period (K Period for Stoch)", minval=1, group=g_stoch)
stoch_k_smooth = input.int(3, "Stochastic %K Smoothing (D Period for Stoch)", minval=1, group=g_stoch)
stoch_d_smooth = input.int(3, "Stochastic %D Smoothing (Smoothing for final D)", minval=1, group=g_stoch)

g_signal = "Signal Trigger and Confirmation Parameters"
stoch_5min_k_long_trigger = input.float(40.0, "5-min Stoch K Long Trigger Level (K must be ≤ this value)", minval=0, maxval=100, step=0.1, group=g_signal, tooltip="On the 5-minute chart, when the K line crosses above the D line, the K value at that time must be less than or equal to this setting to initiate a long signal wait.")
stoch_5min_k_short_trigger = input.float(70.0, "5-min Stoch K Short Trigger Level (K must be ≥ this value)", minval=0, maxval=100, step=0.1, group=g_signal, tooltip="On the 5-minute chart, when the K line crosses below the D line, the K value at that time must be greater than or equal to this setting to initiate a short signal wait.")
stoch_15min_long_entry_level = input.int(50, "15-min Stoch K Long Confirmation Threshold (K must be below this value)", minval=0, maxval=100, group=g_signal, tooltip="On the 15-minute chart, for final long confirmation, the 15-minute K line value must be below this setting.")
stoch_15min_short_entry_level = input.int(70, "15-min Stoch K Short Confirmation Threshold (K must be above this value)", minval=0, maxval=100, group=g_signal, tooltip="On the 15-minute chart, for final short confirmation, the 15-minute K line value must be above this setting.")
wait_window_5min_bars = input.int(7, "Number of 5-min bars to wait for 15-min signal", minval=1, group=g_signal, tooltip="After a 5-minute signal is issued, wait for 15-minute signal confirmation within the next N 5-minute bars.")

g_repeat_filter = "Duplicate Signal Filtering Settings"
use_signal_cooldown_filter = input.bool(true, title="Enable Duplicate Signal Filter", group=g_repeat_filter, tooltip="Filters out duplicate signals in the same direction within a short period.")
min_bars_between_signals = input.int(12, title="Minimum Bars Between Same-Direction Signals", minval=1, group=g_repeat_filter, tooltip="After a signal is issued, at least this many bars must pass before another signal in the same direction can be issued.")

// --- Take Profit Parameters ---
g_tp_params = "Take Profit Parameters"
extreme_long_tp_level = input.float(95.0, "Extreme Long TP Level (Stoch K >)", minval=50, maxval=100, step=0.1, group=g_tp_params, tooltip="Direct TP for longs if 5-min OR 15-min Stoch K exceeds this.")
extreme_short_tp_level = input.float(5.0, "Extreme Short TP Level (Stoch K <)", minval=0, maxval=50, step=0.1, group=g_tp_params, tooltip="Direct TP for shorts if 5-min OR 15-min Stoch K is below this.")

// --- Strategy Specific Input Parameters ---
g_strategy = "Strategy Parameters"
leverage_multiplier = input.float(1.0, "Leverage Multiplier (Affects theoretical position size only)", minval=1.0, step=0.1, group=g_strategy, tooltip="Note: TradingView strategies do not directly simulate margin account liquidation. This leverage is used to calculate theoretical position size. Actual leverage effects must be realized with a broker that supports leverage.")

// --- Function: Calculate Stochastic RSI ---
getStochasticRSI(src, rsiLen, stochLen, kSmooth, dSmooth) =>
    rsi_val = ta.rsi(src, rsiLen)
    stoch_rsi_k_raw = ta.stoch(rsi_val, rsi_val, rsi_val, stochLen) // Stoch of RSI
    stoch_rsi_k = ta.sma(stoch_rsi_k_raw, kSmooth)
    stoch_rsi_d = ta.sma(stoch_rsi_k, dSmooth)
    [stoch_rsi_k, stoch_rsi_d]

// --- Helper Function to get only K-series for Stochastic RSI (RE-ADDED for 15-min prev K) ---
getStochKSeriesOnly(src, rsiLen, stochLen, kSmooth, dSmooth) =>
    rsi_val = ta.rsi(src, rsiLen)
    stoch_rsi_k_raw = ta.stoch(rsi_val, rsi_val, rsi_val, stochLen)
    stoch_rsi_k = ta.sma(stoch_rsi_k_raw, kSmooth)
    stoch_rsi_k // Return only the K series

// --- Time Series Data Fetching and Stochastic RSI Calculation ---
[stoch_k_15min_val, stoch_d_15min_val] = request.security(syminfo.tickerid, "15", getStochasticRSI(close, rsi_len, stoch_rsi_len, stoch_k_smooth, stoch_d_smooth), lookahead=barmerge.lookahead_off)
// RE-ADDED: K value of the PREVIOUS 15-minute bar's Stochastic RSI
stoch_k_15min_prev_tf_bar = request.security(syminfo.tickerid, "15", nz(getStochKSeriesOnly(close, rsi_len, stoch_rsi_len, stoch_k_smooth, stoch_d_smooth)[1]), lookahead=barmerge.lookahead_off)
[stoch_k_5min_val, stoch_d_5min_val] = getStochasticRSI(close, rsi_len, stoch_rsi_len, stoch_k_smooth, stoch_d_smooth)

// --- Signal Logic State Variables ---
var bool waiting_for_15m_long_confirm = false
var bool waiting_for_15m_short_confirm = false
var int bars_elapsed_in_wait_state = 0
var int last_long_signal_bar_idx = -min_bars_between_signals 
var int last_short_signal_bar_idx = -min_bars_between_signals

// --- Variables to store SL reference points from ENTRY bar ---
var float entry_bar_low_for_sl = na
var float entry_bar_high_for_sl = na

// --- Take Profit Logic State Variables ---
var bool first_tp_long_taken = false
var bool first_tp_short_taken = false
// RE-ADDED: State variables for pending TP confirmation on 15-min reversal
var bool pending_long_tp_on_15m_reversal = false
var bool pending_short_tp_on_15m_reversal = false

// --- Detect 5-minute Stochastic RSI crossover events for ENTRY ---
bool stoch_5min_crossed_up_prev_bar = ta.crossover(stoch_k_5min_val[1], stoch_d_5min_val[1])
bool stoch_5min_crossed_down_prev_bar = ta.crossunder(stoch_k_5min_val[1], stoch_d_5min_val[1])
bool condition_5min_k_level_for_long_trigger = stoch_k_5min_val[1] <= stoch_5min_k_long_trigger
bool condition_5min_k_level_for_short_trigger = stoch_k_5min_val[1] >= stoch_5min_k_short_trigger

// --- Specific 5-minute Stochastic RSI crossover for Take Profit (current bar) ---
bool stoch_5min_k_cross_under_d_tp = ta.crossunder(stoch_k_5min_val, stoch_d_5min_val) // For Long TP trigger
bool stoch_5min_k_cross_over_d_tp  = ta.crossover(stoch_k_5min_val, stoch_d_5min_val)  // For Short TP trigger

// --- RE-ADDED: 15-minute Reversal Confirmation for Take Profit ---
bool confirm_15m_reversal_for_long_tp = stoch_k_15min_val < stoch_k_15min_prev_tf_bar
bool confirm_15m_reversal_for_short_tp = stoch_k_15min_val > stoch_k_15min_prev_tf_bar

// --- Manage waiting state and tolerance period for ENTRY ---
if (strategy.position_size == 0)
    if (stoch_5min_crossed_up_prev_bar and condition_5min_k_level_for_long_trigger)
        can_trigger_new_long = not use_signal_cooldown_filter or (bar_index - last_long_signal_bar_idx >= min_bars_between_signals)
        if (can_trigger_new_long)
            waiting_for_15m_long_confirm := true
            waiting_for_15m_short_confirm := false 
            bars_elapsed_in_wait_state := 1
    else if (stoch_5min_crossed_down_prev_bar and condition_5min_k_level_for_short_trigger)
        can_trigger_new_short = not use_signal_cooldown_filter or (bar_index - last_short_signal_bar_idx >= min_bars_between_signals)
        if (can_trigger_new_short)
            waiting_for_15m_short_confirm := true
            waiting_for_15m_long_confirm := false 
            bars_elapsed_in_wait_state := 1
    else if (waiting_for_15m_long_confirm or waiting_for_15m_short_confirm)
        bars_elapsed_in_wait_state += 1
    
    if (bars_elapsed_in_wait_state > wait_window_5min_bars)
        waiting_for_15m_long_confirm := false
        waiting_for_15m_short_confirm := false
        bars_elapsed_in_wait_state := 0 
else 
    waiting_for_15m_long_confirm := false
    waiting_for_15m_short_confirm := false
    bars_elapsed_in_wait_state := 0

// --- 15-minute Stochastic RSI confirmation conditions for ENTRY (Strict Crossover) ---
bool confirm_15min_long_stoch_kd_cond = stoch_k_15min_val > stoch_d_15min_val // K must be strictly greater than D
bool confirm_15min_short_stoch_kd_cond = stoch_k_15min_val < stoch_d_15min_val // K must be strictly less than D
bool filter_15min_stoch_level_long = stoch_k_15min_val < stoch_15min_long_entry_level
bool filter_15min_stoch_level_short = stoch_k_15min_val > stoch_15min_short_entry_level

// --- Main Signal Determination (for strategy logic) ---
entry_long_signal = false
entry_short_signal = false

if (strategy.position_size == 0) 
    if (waiting_for_15m_long_confirm and bars_elapsed_in_wait_state <= wait_window_5min_bars)
        if (confirm_15min_long_stoch_kd_cond and filter_15min_stoch_level_long)
            can_confirm_new_long = not use_signal_cooldown_filter or (bar_index - last_long_signal_bar_idx >= min_bars_between_signals)
            if (can_confirm_new_long)
                entry_long_signal := true
    if (waiting_for_15m_short_confirm and bars_elapsed_in_wait_state <= wait_window_5min_bars)
        if (confirm_15min_short_stoch_kd_cond and filter_15min_stoch_level_short)
            can_confirm_new_short = not use_signal_cooldown_filter or (bar_index - last_short_signal_bar_idx >= min_bars_between_signals)
            if (can_confirm_new_short)
                entry_short_signal := true

// --- Strategy Execution Logic ---
// Reset SL ref and TP flags if position just closed
if (strategy.position_size == 0 and strategy.position_size[1] != 0) 
    first_tp_long_taken := false
    first_tp_short_taken := false
    entry_bar_low_for_sl := na 
    entry_bar_high_for_sl := na 
    pending_long_tp_on_15m_reversal := false // Reset pending TP flag
    pending_short_tp_on_15m_reversal := false // Reset pending TP flag

if (entry_long_signal) 
    strategy.entry("LE", strategy.long, comment="Long Entry")
    last_long_signal_bar_idx := bar_index 
    waiting_for_15m_long_confirm := false 
    bars_elapsed_in_wait_state := 0
    first_tp_long_taken := false 
    entry_bar_low_for_sl := low 
    entry_bar_high_for_sl := na 
    pending_long_tp_on_15m_reversal := false // Reset for new trade
    pending_short_tp_on_15m_reversal := false     

if (entry_short_signal) 
    strategy.entry("SE", strategy.short, comment="Short Entry")
    last_short_signal_bar_idx := bar_index 
    waiting_for_15m_short_confirm := false 
    bars_elapsed_in_wait_state := 0
    first_tp_short_taken := false  
    entry_bar_high_for_sl := high 
    entry_bar_low_for_sl := na    
    pending_short_tp_on_15m_reversal := false // Reset for new trade
    pending_long_tp_on_15m_reversal := false

// --- Stop Loss Logic (PRIORITY 1) ---
// Check and execute SL first. If SL triggers, position size becomes 0, preventing TP logic below from executing on the same bar.
bool sl_triggered_this_bar = false
if (strategy.position_size > 0) // If in a long trade
    if (not na(entry_bar_low_for_sl) and close < entry_bar_low_for_sl)
        strategy.close(id="LE", comment="SL Long")
        sl_triggered_this_bar := true
        pending_long_tp_on_15m_reversal := false // Ensure pending TP is cancelled if SL hits

if (strategy.position_size < 0) // If in a short trade
    if (not na(entry_bar_high_for_sl) and close > entry_bar_high_for_sl)
        strategy.close(id="SE", comment="SL Short")
        sl_triggered_this_bar := true
        pending_short_tp_on_15m_reversal := false // Ensure pending TP is cancelled if SL hits

// --- Take Profit Logic (PRIORITY 2 - only if SL did not trigger on this bar) ---
if (not sl_triggered_this_bar) // Only proceed with TP if SL hasn't already closed the position on this bar
    if (strategy.position_size > 0) // --- LONG TP LOGIC ---
        extreme_long_tp_condition = stoch_k_5min_val > extreme_long_tp_level or stoch_k_15min_val > extreme_long_tp_level
        if (extreme_long_tp_condition)
            if (not first_tp_long_taken)
                strategy.close(id="LE", comment="TP1 Long", qty_percent=50)
                first_tp_long_taken := true
            else 
                strategy.close(id="LE", comment="TP2 Long")
            pending_long_tp_on_15m_reversal := false // Reset pending state as this TP takes precedence
        else 
            // Conditional TP logic (5-min trigger + 15-min reversal)
            if (stoch_5min_k_cross_under_d_tp and not pending_long_tp_on_15m_reversal) // Set pending state
                pending_long_tp_on_15m_reversal := true
        
            if (pending_long_tp_on_15m_reversal and confirm_15m_reversal_for_long_tp) // Check for confirmation
                if (not first_tp_long_taken)
                    strategy.close(id="LE", comment="TP1 Long", qty_percent=50)
                    first_tp_long_taken := true
                else 
                    strategy.close(id="LE", comment="TP2 Long")
                pending_long_tp_on_15m_reversal := false // Reset after TP

    if (strategy.position_size < 0) // --- SHORT TP LOGIC ---
        extreme_short_tp_condition = stoch_k_5min_val < extreme_short_tp_level or stoch_k_15min_val < extreme_short_tp_level
        if (extreme_short_tp_condition)
            if (not first_tp_short_taken)
                strategy.close(id="SE", comment="TP1 Short", qty_percent=50)
                first_tp_short_taken := true
            else 
                strategy.close(id="SE", comment="TP2 Short")
            pending_short_tp_on_15m_reversal := false // Reset pending state
        else
            // Conditional TP logic (5-min trigger + 15-min reversal)
            if (stoch_5min_k_cross_over_d_tp and not pending_short_tp_on_15m_reversal) // Set pending state
                pending_short_tp_on_15m_reversal := true

            if (pending_short_tp_on_15m_reversal and confirm_15m_reversal_for_short_tp) // Check for confirmation
                if (not first_tp_short_taken)
                    strategy.close(id="SE", comment="TP1 Short", qty_percent=50)
                    first_tp_short_taken := true
                else 
                    strategy.close(id="SE", comment="TP2 Short")
                pending_short_tp_on_15m_reversal := false // Reset after TP