Relative Strength Index (RSI)
Background
The Relative Strength Index (RSI) is a momentum oscillator used in technical analysis to measure the speed and change of price movements of an asset. It ranges from 0 to 100 and is typically used to identify overbought or oversold conditions in a market.
Typically, a value above 70 indicates that an asset is overbought, suggesting the price will fall, while a value below 30 indicates that it is oversold, suggesting the price will rise.
Traders use RSI to identify potential reversal points, overbought and oversold conditions, and to confirm trends.
![RSI](/docs/rsi.png)
Relative Strength Index Strategy
The RSI period for our strategy is 14. This means we calculate the RSI value for every 14 blocks.
When self.rsi
is less than 30, we set self.buying
to True
. Further into the program, we check if self.buying
is True
, in which case
we return a trade order to convert asset x to y. This only happens if we have enough x tokens (more than 0).
Similarly, when self.rsi
is greater than 70, we set self.selling
to True
. Further into the program, we check if self.selling
is True
,
in which case we return a trade order to convert asset y to x. This only happens if we have enough y tokens (more than 0).
![RSI Simulation Rewards](/docs/rsi-sim1.png)
How To Run
Installation
Follow our Getting Started guide to install the dojo library and other required tools.
Then clone the dojo_examples
repository and go into the relevant directory.
git clone https://github.com/CompassLabs/dojo_examples.git
cd dojo_examples/examples/rsi
To run this trading strategy on the Arbitrum network, change the
chain
variable in your .env file toarbitrum
and setarbitrum_rpc_url
. Currently, arbitrum can only be used with theforked
backend.
Running
Download the dashboard to view the simulation results.
To view example simulation data, download results.db
and click 'Add A Simulation' on the dashboard.
To run the simulation yourself, use the following command.
python run.py
This command will setup your local blockchain, contracts, accounts and agents. You can then access your results on your Dojo dashboard by connecting to a running simulation.
Step-By-Step Explanation
Initialization
We create a class called RSIPolicy
which inherits from the BasePolicy
class and initializes some variables that will be used later on.
# a policy that uses the RSI indicator to make decisions
class RSIPolicy(BasePolicy):
"""RSI trading policy for a UniswapV3Env with a single pool.
:param agent: The agent which is using this policy.
"""
def __init__(self, agent: BaseAgent):
self.agent = agent
self.rsi_period = 14
self.rsi_values = deque(maxlen=self.rsi_period)
self.rsi = 0
self.buying = False
self.selling = False
Signal Calculation
Signals allow us to easily view data on our Dojo dashboard. In this example, we are adding a signal to monitor the RSI value over time. We can then add bookmarks on the dashboard to view when a trade was made and at what RSI value.
![RSI Indicator Signals](/docs/rsi-sim2.png)
pool = obs.pools[0]
token0, token1 = obs.pool_tokens(pool)
# calculate RSI
self.rsi_values.append(obs.price(token1, token0, pool))
if len(self.rsi_values) == self.rsi_period:
delta = np.diff(self.rsi_values)
gains = delta[delta > 0]
losses = -delta[delta < 0]
if losses.size == 0:
self.rsi = 100
elif gains.size == 0:
self.rsi = 0
else:
gain = Decimal(gains.mean())
loss = Decimal(losses.mean())
rs = gain / loss
self.rsi = 100 - 100 / (1 + rs)
obs.add_signal("RSI", self.rsi)
Trade Execution
The RSI value being less than 30 shows us that the token is oversold and undervalued. Therefore, we set the buying variable to True. Then, if the agent doesn't have enough balance to trade, we return an empty list which means do no trades. Otherwise, we return a UniswapV3Trade
object specifying the agent, the pool and the amount of each token to buy/sell.
# make decision
if self.rsi < 30:
self.buying = True
elif self.rsi > 70:
self.selling = True
# execute action
if self.buying:
self.buying = False
if self.agent.quantity(token0) == Decimal(0):
return []
return [
UniswapV3Trade(
self.agent, pool, (Decimal(self.agent.quantity(token0)), Decimal(0))
)
]
elif self.selling:
self.selling = False
if self.agent.quantity(token1) == Decimal(0):
return []
return [
UniswapV3Trade(
self.agent, pool, (Decimal(0), Decimal(self.agent.quantity(token1)))
)
]
return []
In the USDC/WETH pool, returning a UniswapV3Trade
object with quantities=(Decimal(self.agent.quantity(token0)), Decimal(0))
means we are swapping all of our USDC tokens for WETH tokens, essentially buying WETH. The converse is true for selling WETH.
In the run.py
file, we create a pool, a Uniswap environment and an agent that implements the RSI policy.
Results
You can download the results to this example below.
We offer a dashboard desktop application for visualizing your simulation results. This downloaded file can be added to the dashboard application.