Skip to content

RSI Trading Bot Example

Imports

The first thing we do is, import all the necessary modules

bot.py
import logging
from strategytester5.MetaTrader5.api import VirtualMetaTrader5
from strategytester5.tester import run_backtesting
from strategytester5.trade_classes.Trade import CTrade
import MetaTrader5 as parent_mt5
import pandas as pd
from ta.momentum import rsi
import sys

Note

From momentum submodule we import a method called rsi which calculates the indicator.

from ta.momentum import rsi

MetaTrader5 Initialization

Note

Despite the strategytester5 framework simulating the MetaTrader5 terminal, it still relies of the platform for crucial information from instruments (symbols) and a broker's account.

This is a crucial step that shouldn't be missed.

if not parent_mt5.initialize():
    raise RuntimeError(f"Failed to initialize mt5. Error = {parent_mt5.last_error()}")

VirtualMetaTrader5

To be able to backtest and live trade on a single file you have to choose the appropriate MetaTrader5 to use.

script_argument = sys.argv[1:]
if "--backtesting" in script_argument:
    mt5 = VirtualMetaTrader5(parent_mt5=parent_mt5) # Assign parent MetaTrader5 to the virtual MetaTrader5 class object
else:
    mt5 = parent_mt5

Optional | Global variables and the CTrade helper

symbol = "EURUSD"
timeframe = mt5.TIMEFRAME_H1 # This should be an integer so you should convert timeframe in string into integer

# ---------------------------------------------------------

MAGIC_NUMBER = 1001
m_trade = CTrade(terminal=mt5, symbol=symbol, magic_number=MAGIC_NUMBER, deviation_points=100)

RSI Reversal Strategy

The strategy is simple; We open a long trade when the price reaches the oversold threshold and do the opposite when the price reaches an overbought threshold, we open a short trade.

The common thresholds are usually 30.0 and 70.0 for oversold and overbought respectively. In other words, these are short and sold signals respectively.

When a long signal is receved we close short trades and when a short signal is received we do the same but, oppposite, close a long trade.

Not to mention, we open a single trade (position) in one direction at a time.

bot.py
def pos_exists(magic: int, pos_type: int) -> bool:
    """Check if position exists"""
    positions_found = mt5.positions_get()
    for position in positions_found:
        if position.type == pos_type and position.magic == magic:
            return True

    return False

def close_pos_by_type(magic: int, pos_type: int):
    """Close positions by type"""
    positions_found = mt5.positions_get()
    for position in positions_found:
        if position.type == pos_type and position.magic == magic:
            m_trade.position_close(position.ticket)


def main():

    indicator_window = 14
    rates = mt5.copy_rates_from_pos(symbol, timeframe, 0, indicator_window)

    if rates is None or len(rates) < indicator_window: # if no information was found, or less than expected rates were returned
        return # prevent further calculations

    rates_df = pd.DataFrame(data=rates)
    rsi_value = rsi(close=rates_df["close"], window=indicator_window).iloc[-1]

    # rsi strategy

    rsi_oversold = 30.0
    rsi_overbought = 70.0

    symbol_info = mt5.symbol_info(symbol)
    lot_size = symbol_info.volume_min


    if rsi_value < rsi_oversold: # long signal
        if not pos_exists(magic=MAGIC_NUMBER, pos_type=mt5.POSITION_TYPE_BUY):
            m_trade.buy(volume=lot_size, price=symbol_info.ask)

        close_pos_by_type(magic=MAGIC_NUMBER, pos_type=mt5.POSITION_TYPE_SELL)

    if rsi_value > rsi_overbought: # short signal
        if not pos_exists(magic=MAGIC_NUMBER, pos_type=mt5.POSITION_TYPE_SELL):
            m_trade.sell(volume=lot_size, price=symbol_info.bid)

        close_pos_by_type(magic=MAGIC_NUMBER, pos_type=mt5.POSITION_TYPE_BUY)

Backtesting

Learn about tester configurations.

tester_config = {
        "bot_name": "RSI Strategy Bot",
        "symbols": ["EURUSD"],
        "timeframe": "H1",
        "start_date": "01.01.2026",
        "end_date": "01.06.2026",
        "modelling" : "open price only",
        "deposit": 1000,
        "leverage": "1:100"
}

if "--backtesting" in script_argument:

    stats = run_backtesting(
        main_function=main,
        tester_config=tester_config,
        virtual_mt5=mt5,
        logging_level=logging.DEBUG
    )

Running the script in the command prompt with parameter(s) --backtesting, will open a browser tab on localhost, from there your trading operations will be visualized.

report

Other Working examples and trading robots can be found here.