arsalandywriter.com

Trading the RSI Intelligently: A Python Analysis and Enhancement

Written on

In the realm of technical analysis, refining indicators can yield deeper insights and superior signals. This article delves into a minor adjustment to the Relative Strength Index (RSI) and juxtaposes the results against the conventional RSI approach.

Following the success of my prior publication, I have released a new book featuring advanced trend-following indicators and strategies, complete with a dedicated GitHub page for ongoing code updates. Additionally, this edition has been optimized for printing, retaining the original colors. If this piques your interest, feel free to check out the Amazon link below or reach out to me on LinkedIn for a PDF version.

Trend Following Strategies in Python: How to Use Indicators to Follow the Trend.

Amazon.com: Trend Following Strategies in Python: How to Use Indicators to Follow the Trend.: 9798756939620: Kaabar… www.amazon.com

Understanding the Relative Strength Index

The RSI stands out as the most recognized momentum indicator, especially effective in ranging markets. Its values span from 0 to 100, simplifying interpretation. The widespread use of the RSI among traders can create a self-fulfilling prophecy, as market movements often respond to its signals.

The calculation of RSI is straightforward. We begin by determining the price differences for each period by subtracting each closing price from the preceding one. Next, we compute the smoothed average of positive differences and divide that by the smoothed average of negative differences. This yields the Relative Strength, which is then plugged into the RSI formula to produce a value between 0 and 100.

To compute the RSI, an OHLC array (not a data frame) is required. This entails working with a four-column array. The function for calculating the RSI is as follows:

# Function to add a specified number of columns to an array def adder(Data, times):

for i in range(1, times + 1):

new_col = np.zeros((len(Data), 1), dtype=float)

Data = np.append(Data, new_col, axis=1)

return Data

# Function to delete a specified number of columns starting from an index def deleter(Data, index, times):

for i in range(1, times + 1):

Data = np.delete(Data, index, axis=1)

return Data

# Function to remove a specified number of rows from the beginning def jump(Data, jump):

Data = Data[jump:, ]

return Data

# Example of adding 3 empty columns to an array my_ohlc_array = adder(my_ohlc_array, 3)

# Example of deleting the 2 columns after the column indexed at 3 my_ohlc_array = deleter(my_ohlc_array, 3, 2)

# Example of deleting the first 20 rows my_ohlc_array = jump(my_ohlc_array, 20)

The OHLC acronym stands for Open, High, Low, and Close, representing standard historical data. The moving average function can be defined as:

def ma(Data, lookback, close, where):

Data = adder(Data, 1)

for i in range(len(Data)):

try:

Data[i, where] = (Data[i - lookback + 1:i + 1, close].mean())

except IndexError:

pass

Data = jump(Data, lookback)

return Data

def ema(Data, alpha, lookback, what, where):

alpha = alpha / (lookback + 1.0)

beta = 1 - alpha

Data = ma(Data, lookback, what, where)

Data[lookback + 1, where] = (Data[lookback + 1, what] * alpha) + (Data[lookback, where] * beta)

for i in range(lookback + 2, len(Data)):

try:

Data[i, where] = (Data[i, what] * alpha) + (Data[i - 1, where] * beta)

except IndexError:

pass

return Data

def rsi(Data, lookback, close, where, width=1, genre='Smoothed'):

Data = adder(Data, 7)

for i in range(len(Data)):

Data[i, where] = Data[i, close] - Data[i - width, close]

for i in range(len(Data)):

if Data[i, where] > 0:

Data[i, where + 1] = Data[i, where]

elif Data[i, where] < 0:

Data[i, where + 2] = abs(Data[i, where])

if genre == 'Smoothed':

lookback = (lookback * 2) - 1

Data = ema(Data, 2, lookback, where + 1, where + 3)

Data = ema(Data, 2, lookback, where + 2, where + 4)

if genre == 'Simple':

Data = ma(Data, lookback, where + 1, where + 3)

Data = ma(Data, lookback, where + 2, where + 4)

Data[:, where + 5] = Data[:, where + 3] / Data[:, where + 4]

Data[:, where + 6] = (100 - (100 / (1 + Data[:, where + 5])))

Data = deleter(Data, where, 6)

Data = jump(Data, lookback)

return Data

Medium serves as a platform for diverse interesting reads. I explored numerous articles before embarking on my own writing journey. Consider joining Medium through my referral link!

Introduction to Correlation

Correlation represents the extent of a linear relationship between two or more variables. It is confined between -1 and 1, with 1 indicating a perfect positive correlation, -1 representing a perfect negative correlation, and 0 denoting no linear correlation. While this measure isn't flawless and may be affected by outliers and non-linear relationships, it provides quick insights into statistical properties. Two well-known types of correlation are:

  • Spearman correlation assesses the relationship between two continuous or ordinal variables, where variables may vary together but not necessarily at a constant rate, relying on value ranks.
  • Pearson correlation evaluates the linear relationship between two continuous variables, characterized by proportional changes in each variable.

Although not perfect, this measure allows for swift insights into statistical characteristics.

In Python, we can implement a correlation function for two variables as follows, ensuring it utilizes an array rather than a data frame:

from scipy.stats import pearsonr

def rolling_correlation(Data, first_data, second_data, lookback, where):

Data = adder(Data, 1)

for i in range(len(Data)):

try:

Data[i, where] = pearsonr(Data[i - lookback + 1:i + 1, first_data], Data[i - lookback + 1:i + 1, second_data])[0]

except ValueError:

pass

Data = jump(Data, lookback)

return Data

If you're interested in further technical indicators and strategies, my book may appeal to you:

The Book of Trading Strategies

Amazon.com: The Book of Trading Strategies: 9798532885707: Kaabar, Sofien: Books www.amazon.com

The Correlation-Adjusted Relative Strength Index

The fundamental concept of this indicator is that when the correlation between the indicator and market price is high, it’s prudent to heed its signals. The strategy encompasses these steps:

  1. Calculate a 2-period RSI on the market price.
  2. Determine a 3-period rolling correlation between the market price and its 2-period RSI to assess their movement together.
  3. Compute a 3-period RSI on the rolling correlation established in the previous step.

> Note: The selection of lookback periods is subjective and can vary. For manual trading, a 13-period RSI may be employed.

# Indicator Parameters lookback_corr = 3 lookback_rsi = 2

# Calculating the 2-period RSI on the market price my_data = rsi(my_data, lookback_rsi, 3, 4)

# Determining the rolling 3-period correlation between market price and 2-period RSI my_data = rolling_correlation(my_data, 3, 4, lookback_corr, 5)

# Computing the 3-period RSI of the 3-period correlation my_data = rsi(my_data, lookback_corr, 5, 6)

The trading criteria can be summarized as follows:

  • A buy (Long) signal triggers when the 2-period RSI is at or below 5, while the 3-period RSI of the correlation is at or above 50.
  • A sell (Short) signal occurs when the 2-period RSI is at or above 95, with the 3-period RSI of the correlation at or above 50.

The high correlation condition serves to filter signals, suggesting that the current RSI is more predictive when the correlation with market price is elevated. Conversely, a low correlation implies a likely lack of reaction in line with the expected outcome. The signal function can be structured as follows:

def signal(Data, rsi_col, corr_rsi, buy, sell):

Data = adder(Data, 10)

for i in range(len(Data)):

if Data[i, rsi_col] <= 5 and Data[i, corr_rsi] > 50 and Data[i - 1, buy] == 0 and Data[i - 2, buy] == 0 and Data[i - 3, buy] == 0 and Data[i - 4, buy] == 0:

Data[i, buy] = 1

elif Data[i, rsi_col] >= 95 and Data[i, corr_rsi] > 50 and Data[i - 1, sell] == 0 and Data[i - 2, sell] == 0 and Data[i - 3, sell] == 0 and Data[i - 4, sell] == 0:

Data[i, sell] = -1

return Data

Two methods for validating this concept include:

  1. Comparing the above strategy with a conventional 2-period RSI strategy without correlation. This provides a direct comparison, albeit without the correlation filter, highlighting the added value of this technique.
  2. Comparing the strategy while inverting the condition of the correlation, meaning we will only consider signals if the RSI of the correlation is below 50.

The first test conducted on hourly EURUSD values employs a 1-hour holding period, assuming the short-term RSI serves as a timing indicator. Each signal entails buying or short-selling and holding until the subsequent bar. The results indicate that the correlation filter performs better. However, this doesn’t establish a solid strategy since the equity curve below is based solely on simple rules without risk management or accounting for transaction costs.

The second test juxtaposes the correlation-adjusted RSI against a modified correlation condition, further validating our initial intuition.

For more insights on trading strategies, consider subscribing to my DAILY Newsletter (a Free Plan is Available) via the link below. It includes my Medium articles, additional trading strategies, coding lessons related to research and analysis, and subscribers receive a free PDF copy of my first book. Expect 5–7 articles weekly with a paid subscription, and 1–2 articles weekly with the free plan. Your support enables me to continue sharing my research. Thank you!

All About Trading!

Sharing Trading Strategies, Knowledge, and Technical Tools in Detail. abouttrading.substack.com

Always conduct your back-tests. It's essential to question the reliability of others' strategies. My indicators and trading style may suit me but may not align with your approach.

I firmly believe in self-discovery through practice rather than mere imitation. Grasp the concept, function, intuition, and conditions of a strategy, then develop an even better version for thorough back-testing and refinement before implementation or rejection. My decision to withhold specific back-testing results encourages readers to engage deeply with the strategy and develop it further.

A Final Note

I have recently launched an NFT collection aimed at supporting various humanitarian and medical initiatives. The Society of Light is a series of limited collectibles designed to contribute positively to the world, with a portion of each sale directed to the associated charity. Here are some benefits of investing in these NFTs:

  • High-potential gain: By channeling the remaining sales proceeds into promoting The Society of Light, I aim to enhance their value in the secondary market. Remember that trading in the secondary market also contributes royalties to the same charity.
  • Art collection and portfolio diversification: Owning avatars that symbolize good deeds is deeply fulfilling. Investing can transcend mere profit motives—why not invest to make money, assist others, and collect art?
  • Donating to your preferred cause(s): This provides a flexible means of distributing funds to your chosen charities.
  • A free copy of my book in PDF: Every NFT purchaser will receive a complimentary copy of my latest book linked in this article.
Support humanitarian causes through NFT purchases.

Share the page:

Twitter Facebook Reddit LinkIn

-----------------------

Recent Post:

Embracing Change: A Journey from Shadows to Light

A heartfelt exploration of transformation, letting go of past burdens, and seeking a return to innocence.

Innovative Solutions for the Future of Transportation

Explore the latest advancements in transportation and how they promise to shape the future, ensuring efficiency and sustainability.

Exploring Philosophical Perspectives on War and Peace

A look at historical philosophical views on war and peace and their implications for society today, highlighting key thinkers and their contributions.

React Hooks: A Critical Examination of Their Design Choices

Analyzing the design principles of React Hooks and their implications on code structure and performance.

How to Stay Agile and Active as You Age: A Kinesiologist's Guide

Discover how to maintain mobility and reduce stiffness as you age with practical tips and exercises for a healthier lifestyle.

Reflections on 1 and 2 Thessalonians: Embracing Faith and Community

Explore the teachings of Paul in 1 and 2 Thessalonians, emphasizing faith, community support, and living in anticipation of Christ's return.

Understanding the

Explore the significance and mechanics of Python's

Effective iOS Crash Reporting Tools: Fast Bug Detection and Fixes

Discover top iOS crash reporting tools to quickly identify and resolve bugs, enhancing user experience and app stability.