Skip to content

RSI Trading Strategy#

Trading Risk Warning

IMPORTANT: All examples should be tested using demo accounts only!

  • Trading involves substantial risk of loss
  • These examples are for educational purposes only
  • Always test with fake money before using real funds

Overview#

The Relative Strength Index (RSI) is a momentum oscillator that measures the speed and change of price movements. It oscillates between 0 and 100 and is typically used to identify overbought or oversold conditions in a market.

Signal Type Description
Buy signal When RSI falls below the oversold threshold (typically 30)
Sell signal When RSI rises above the overbought threshold (typically 70)

Strategy Logic#

Step Description
1 Calculate the RSI indicator using price data
2 Generate buy signals when RSI falls below the oversold threshold
3 Generate sell signals when RSI rises above the overbought threshold
4 Execute trades only during specified trading hours

Strategy Flow#

flowchart TD
    A[Start] --> B[Fetch Market Data]
    B --> C[Calculate Price Changes]
    C --> D[Separate Gains and Losses]
    D --> E[Calculate Average Gain and Loss]
    E --> F[Calculate RSI Value]
    F --> G{"RSI < Oversold<br/>Threshold?"}
    G -->|Yes| H[Generate Buy Signal]
    G -->|No| I{"RSI > Overbought<br/>Threshold?"}
    I -->|Yes| J[Generate Sell Signal]
    I -->|No| K[No Trading Signal]
    H --> L{"Within Trading<br/>Hours?"}
    J --> L
    K --> L
    L -->|Yes| M[Execute Trade]
    L -->|No| N[Skip Trade Execution]
    M --> O[Update Statistics]
    N --> O
    O --> P[Wait for Next Tick]
    P --> B

Code Implementation#

Let's break down the implementation step by step:

Step 1: Required Imports#

from __future__ import annotations

import logging
import numpy as np

from mqpy.rates import Rates
from mqpy.tick import Tick
from mqpy.trade import Trade

# Configure logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)

We import the necessary modules: - Core MQPy modules for trading and data access - numpy for efficient calculations in our RSI function - logging for tracking the strategy's operation

Step 2: RSI Calculation Function#

def calculate_rsi(prices: list[float], period: int = 14) -> float | None:
    """Calculate the Relative Strength Index."""
    if len(prices) < period + 1:
        return None

    # Calculate price changes
    deltas = np.diff(prices)

    # Separate gains and losses
    gains = np.where(deltas > 0, deltas, 0)
    losses = np.where(deltas < 0, -deltas, 0)

    # Calculate initial average gain and loss
    avg_gain = np.mean(gains[:period])
    avg_loss = np.mean(losses[:period])

    # Avoid division by zero
    if avg_loss == 0:
        return 100

    # Calculate RS and RSI
    rs = avg_gain / avg_loss
    rsi = 100 - (100 / (1 + rs))

    return rsi

This function implements the RSI formula: 1. First, it checks if we have enough price data (at least period + 1 values) 2. It calculates price changes between consecutive closes using np.diff() 3. It separates the price changes into gains (positive changes) and losses (negative changes) 4. It calculates the average gain and average loss over the specified period 5. It computes the Relative Strength (RS) as the ratio of average gain to average loss 6. Finally, it converts RS to RSI using the formula: RSI = 100 - (100 / (1 + RS))

Step 3: Initialize the Trading Strategy#

trade = Trade(
    expert_name="RSI Strategy",
    version="1.0",
    symbol="EURUSD",
    magic_number=568,
    lot=0.1,
    stop_loss=30,
    emergency_stop_loss=90,
    take_profit=60,
    emergency_take_profit=180,
    start_time="9:15",
    finishing_time="17:30",
    ending_time="17:50",
    fee=0.5,
)

We configure our trading strategy with: - Identification parameters: name, version, magic number - Trading parameters: symbol, lot size - Risk management parameters: stop loss and take profit (notice the take profit is 2x the stop loss) - Trading session times: when to start, when to stop opening new positions, and when to close all positions

Step 4: Set Strategy Parameters#

# Strategy parameters
prev_tick_time = 0
rsi_period = 14
overbought_threshold = 70
oversold_threshold = 30

The key parameters for our RSI strategy are: - rsi_period: The number of periods for RSI calculation (standard is 14) - overbought_threshold: The RSI level above which we consider the market overbought (70) - oversold_threshold: The RSI level below which we consider the market oversold (30)

Step 5: Main Trading Loop#

try:
    while True:
        # Prepare the symbol for trading
        trade.prepare_symbol()

        # Fetch tick and rates data
        current_tick = Tick(trade.symbol)
        historical_rates = Rates(trade.symbol, rsi_period + 20, 0, 1)  # Get extra data for reliability

In the main loop, we: - Prepare the symbol for trading - Get the current market price - Retrieve historical price data (we get rsi_period + 20 bars for reliable calculations)

Step 6: Calculate RSI and Generate Signals#

# Only process if we have a new tick and enough data for RSI calculation
if current_tick.time_msc != prev_tick_time and len(historical_rates.close) >= rsi_period + 1:
    # Calculate RSI
    rsi_value = calculate_rsi(historical_rates.close, rsi_period)

    if rsi_value is not None:
        # Generate signals based on RSI thresholds
        is_buy_signal = rsi_value < oversold_threshold
        is_sell_signal = rsi_value > overbought_threshold

        # Log RSI values and signals
        if is_buy_signal:
            logger.info(f"Oversold condition: RSI = {rsi_value:.2f} (< {oversold_threshold})")
        elif is_sell_signal:
            logger.info(f"Overbought condition: RSI = {rsi_value:.2f} (> {overbought_threshold})")
        else:
            logger.debug(f"Current RSI: {rsi_value:.2f}")

For each new tick, we: 1. Calculate the RSI value using our custom function 2. Generate trading signals based on simple threshold comparisons: - Buy when RSI < oversold threshold (30) - Sell when RSI > overbought threshold (70) 3. Log the RSI values and any signals generated for monitoring

Step 7: Execute Trades#

# Execute trading positions based on signals during allowed trading hours
if trade.trading_time():
    trade.open_position(
        should_buy=is_buy_signal,
        should_sell=is_sell_signal,
        comment=f"RSI Strategy: {rsi_value:.2f}"
    )

When a signal is detected: - We check if we're within the allowed trading hours - If yes, we execute the appropriate trade based on our signals - The comment includes the RSI value for reference in the trading terminal

Step 8: Update State and Check for End of Day#

# Update trading statistics periodically
trade.statistics()

prev_tick_time = current_tick.time_msc

# Check if it's the end of the trading day
if trade.days_end():
    trade.close_position("End of the trading day reached.")
    break

After processing each tick, we: - Update the trading statistics for monitoring - Store the current tick time for the next iteration - Check if it's the end of the trading day, and if so, close positions and exit

Step 9: Error Handling#

except KeyboardInterrupt:
    logger.info("Strategy execution interrupted by user.")
    trade.close_position("User interrupted the strategy.")
except Exception as e:
    logger.error(f"Error in strategy execution: {e}")
finally:
    logger.info("Finishing the program.")

Our error handling ensures: - Proper handling of user interruptions - Logging of any errors that occur - Clean program termination in the finally block

Full Source Code#

You can find the complete source code for this strategy in the MQPy GitHub repository.

Optimization Opportunities#

This strategy can be improved by:

Improvement Description
Smoothing Using a smoothed RSI or applying an additional moving average to filter out noise
Trend Filters Only taking trades in the direction of the longer-term trend
Divergence Looking for divergence between price and RSI for stronger signals
Dynamic Thresholds Adjusting the overbought/oversold thresholds based on market volatility
Position Management Taking partial profits when RSI reaches extreme levels

Next Steps#

Try experimenting with:

Experiment Options
RSI Periods Shorter periods (9) for more signals, longer periods (21) for fewer but stronger signals
Threshold Levels Test different levels like 20/80 for stronger signals but fewer trades
Complementary Indicators Add moving averages or other oscillators to confirm RSI signals
Position Sizing Implement different sizing based on the distance of RSI from thresholds