Custom strategy using RSI#

New user? The best way to learn SigTech is to follow the steps in our user guide.

Unrestricted data: This notebook only uses data in our core offer to all users. Go ahead and run it.

Changes will not be saved: Edits made to SigTech’s example notebooks like this will only persist for the duration of your session. When you restart the research environment, this notebook will have been restored to its original state, and any changes you have made will have been lost. To make permanent changes, copy and paste the content to a new notebook in one of your workspaces.

Introduction#

The purpose of this notebook is to show how to create a custom strategy which acts on an RSI indicator.

Environment#

This section will import relevant internal and external libraries, as well as setting up the platform environment.

[ ]:
import sigtech.framework as sig
from sigtech.framework.signal.library.momentum import rsi

import pandas as pd
import datetime as dtm
import seaborn as sns

sns.set(rc={'figure.figsize': (18, 6)})

env = sig.init(env_date=dtm.date(2024, 2, 22))

Universe#

[ ]:
sig_master = env.sig_master()
rdsa = sig_master.filter('RDSA', 'EXCHANGE_TICKER')\
          .filter_primary_tradable() \
          .filter_operating_mic(operating_mics='XLON')
rdsa = rdsa.to_single_stock()
rdsa_xlon = rdsa[list(rdsa.keys())[0]]
[ ]:
rs = sig.ReinvestmentStrategy(
    currency=rdsa_xlon.currency,
    start_date=dtm.date(2010, 1, 4),
    underlyer=rdsa_xlon.name,
)

Strategy#

[ ]:
class CustomRSIStrategy(sig.DailyStrategy):

    asset_ticker: str

    def __post_init__(self):
        self.asset = sig.obj.get(self.asset_ticker)
        self.asset_history = self.asset.history()
        super().__post_init__()

    def strategy_initialization(self, dt):
        """ "Magic" function that is run before the strategy's first trading date. """
        # We loop over every day in our history schedule
        for d in self.history_schedule().all_data_dates()[1:]:
            self.add_method(d, self.rsi_decision)  # a processor is just a method

    def rsi_decision(self, dt):
        """ Takes a trading decision based on RSI level. """

        # Calculate the 14-period RSI
        h = self.asset_history
        d = self.size_date_from_decision_dt(dt)
        asset_history = h[:d].iloc[-20:].to_frame()  # we cut to the last prices
        rsi_series = rsi(14, asset_history).squeeze().dropna()

        # Store today's and yesterday's values
        rsi_tod = rsi_series.iloc[-1]
        rsi_yday = rsi_series.iloc[-2]

        # Decide on how to trade
        # Exiting out of oversold - we buy
        if rsi_tod > 30. and rsi_yday < 30.:
            self.add_position_target(dt, self.asset_ticker, 1., unit_type='WEIGHT')
        # Crossing 50 from beneath, exiting
        elif rsi_tod > 50. and rsi_yday < 50.:
            self.add_position_target(dt, self.asset_ticker, 0., unit_type='WEIGHT')
        # Exiting out of overbought - we sell
        elif rsi_tod < 70. and rsi_yday > 70.:
            self.add_position_target(dt, self.asset_ticker, -1., unit_type='WEIGHT')
        # Crossing 50 from above, exiting
        elif rsi_tod < 50. and rsi_yday > 50.:
            self.add_position_target(dt, self.asset_ticker, 0., unit_type='WEIGHT')
[ ]:
rsi_strat = CustomRSIStrategy(
    currency=rs.currency,
    start_date=dtm.date(2011, 1, 4),
    asset_ticker=rs.name,
    include_trading_costs=False,
)

Performance Analytics#

[ ]:
strategies = pd.concat({
    'RDSA RSI Strategy': rsi_strat.history(),
    'RDSA Long-only Strategy': rs.history()
}, axis=1).dropna()
[ ]:
sig.PerformanceReport(strategies).report()