The “It’s All Relative” Trading Strategy. Combining Momentum and Volatility.
Using Momentum and Volatility Indices to Derive Profitable Signals.
When we want to create a trading strategy, we should find a trade-off between simplicity and complexity, but why is that? Well, the complexity part answers to the question of “What is the minimum amount sophistication of a strategy to be able to perform well in such a nonlinear chaotic market?”, As for the simplicity part, it answers the question of “How can I make this easier so that it makes sense?”
Let us start with a simple idea of incorporating momentum and volatility as components of a trading signal. This means that we will be trading based on signals generated by momentum and volatility indicators. I will first present the two indicators and their intuition before back-testing them in Python.
If you are interested in seeing more strategy combinations, feel free to have a look at the below article:
Momentum Signals: The Relative Strength Index
The first part of the strategy will deal with momentum and is represented by the famous Relative Strength Index. The signals are first generated by this indicator.
The RSI is without a doubt the most famous momentum indicator out there, and this is to be expected as it has many strengths especially in ranging markets. It is also bounded between 0 and 100 which makes it easier to interpret. Also, the fact that it is famous, contributes to its potential.
This is because, the more traders and portfolio managers look at the RSI, the more people will react based on its signals and this in turn can push market prices. Of course, we cannot prove this idea, but it is intuitive as one of the basis of Technical Analysis is that it is self-fulfilling.
The RSI is calculated using a rather simple way. We first start by taking price differences of one period. This means that we have to subtract every closing price from the one before it. Then, we will calculate the smoothed average of the positive differences and divide it by the smoothed average of the negative differences. The last calculation gives us the Relative Strength which is then used in the RSI formula to be transformed into a measure between 0 and 100.


Volatility Signals: The Relative Volatility Index
The second part of the strategy will deal with volatility and is represented by the Relative Volatility Index. The confirmation signals are generated by this indicator.
The first part of creating the RVI is to calculate the Standard Deviation of a certain period. We can use the below code on an OHLC data array to do so:
my_ohlc_array = volatility(my_ohlc_array, 14, 3, 4)# We are saying that we want to apply the volatility function seen above onto our OHLC array using a lookback period of 14 and applying the calculation on the closing price (hence, the fourth column which is indexed at 3). Then, we want to output the result in the fifth empty column which is indexed at 4
The next step is to create two columns to contain the values of when volatility increases or decreases from the last period. Thus, we can define the following conditions:
- If the current Standard Deviation is greater than the previous Standard Deviation, then, input in a new column the current value of the Standard Deviation. Otherwise, input zero. Let us call this column the Positive Change.
- If the current Standard Deviation is lower than the previous Standard Deviation, then, input in a new column the current value of the Standard Deviation. Otherwise, input zero. Let us call this column the Negative Change.
Now that we have two columns where one contains the positive changes in volatility and the other contains the negative changes in volatility, we can proceed by coding them in this way:
for i in range(len(my_ohlc_array)):
if my_ohlc_array[i, std_col] > my_ohlc_array[i - 1, std_col]:
my_ohlc_array[i, positive_ch] = my_ohlc_array[i, std_col]
else:
my_ohlc_array[i, positive_ch] = 0
for i in range(len(my_ohlc_array)):
if my_ohlc_array[i, std_col] < my_ohlc_array[i - 1, std_col]:
my_ohlc_array[i, negative_ch] = my_ohlc_array[i, std_col]
else:
my_ohlc_array[i, negative_ch] = 0# The variable std_col refers to the column where the Standard Deviation is stored
# The variable positive_ch refers to the Positive Change column that will be populated
# The variable negative_ch refers to the Negative Change column that will be populated
Next, we can calculate a simple moving average of the Positive Change and the Negative Change columns. We can also compute a smoothed moving average instead if we want to be exactly in line with the RSI formula, but I like to keep it simple from time to time. Here is how to do so:
def ma(Data, lookback, what, where):
for i in range(len(Data)):
try:
Data[i, where] = (Data[i - lookback + 1:i + 1, what].mean())
except IndexError:
pass
return Datamy_ohlc_array = ma(my_ohlc_array, lookback, positive_ch, up_ma)
my_ohlc_array = ma(my_ohlc_array, lookback, negative_ch, down_ma)# The variable up_ma refers to where we want to store the averaged values of the positive change while the down_ma refers to where we want to store the averaged values of the negative change
All the above work can be combined into one function that defines the Relative Volatility Index:

def relative_volatility_index(Data, lookback, what, where): Data = volatility(Data, lookback, what, where)
for i in range(len(Data)):
if Data[i, where] > Data[i - 1, where]:
Data[i, where + 1] = Data[i, where]
else:
Data[i, where + 1] = 0
for i in range(len(Data)):
if Data[i, where] < Data[i - 1, where]:
Data[i, where + 2] = Data[i, where]
else:
Data[i, where + 2] = 0
Data = ma(Data, lookback, where + 1, where + 3)
Data = ma(Data, lookback, where + 2, where + 4)# RVI
for i in range(len(Data)):
Data[i, where + 5] = 100 * Data[i, where + 3] / (Data[i, where + 3] + Data[i, where + 4])
return Data

Now we will see how to use the RVI with the RSI to create a trading strategy based on signals from both of them.
Creating & Back-testing the Strategy
The intuition of the strategy is to buy on momentum signals with a condition that volatility is low relative to the previous periods and to sell on momentum signals with a condition that volatility is high relative to the previous periods. This is to refer to the saying that volatility is usually negatively correlated to the variable which in case is our market price.

It is not extremely accurate to say that when volatility goes down, the market goes up, as a stable bearish downtrend can also have low volatility and sudden upside spikes can have high volatility.
Hence, our conditions in detail will be:
- Go long (Buy) whenever the 2-period RSI reaches or breaks 10 with the 14-period RVI is below its neutrality level 50%.
- Go short (Sell) whenever the 2-period RSI reaches or surpasses 90 with the 14-period RVI is above its neutrality level 50%.
- Use the Average True Range indicator as risk management system.
def signal(Data, rvi_col, rsi_col, buy, sell):
for i in range(len(Data)):
if Data[i, rsi_col] < lower_barrier and Data[i - 1, rsi_col] > lower_barrier and Data[i - 2, rsi_col] > lower_barrier and \
Data[i, rvi_col] < 50:
Data[i, buy] = 1
if Data[i, rsi_col] > upper_barrier and Data[i - 1, rsi_col] < upper_barrier and Data[i - 2, rsi_col] < upper_barrier and \
Data[i, rvi_col] > 50:
Data[i, sell] = -1

Let us check out the results on the EURUSD and USDCHF hourly OHLC data since January 2010.

Visibly, the strategy delivers some return, even though there are some stale periods where return is moving sideways, but overall, it does seem to show good potential. We can also take a look at the AUDCAD below which seems to be the outperformer of the major currencies.

Conclusion
As always, we back-test and analyze the results. But when do we know that we have a winning strategy? That can be answered by applying the following steps into any back-tested strategy:
- The transaction costs should be represented as accurately as possible. The majority of strategies are sensitive to transaction costs.
- The back-tests should relatively perform well across the time frames due to the self similarity property of the financial markets. This also ensures that you back-test it using different costs.
- The strategies should not be overly sensitive to changes in its variables. For example, if you use a lookback period of 4 on an indicator, then using a lookback period of 5 should not change drastically the results.
Why was this article written? It is certainly not a spoon-feeding method or the way to a profitable strategy. If you follow my articles, you will notice that I place more emphasize on how to do it instead of here it is and that I also provide functions not full replicable code. In the financial industry, you should combine the pieces yourself from other exogenous information and data, only then, will you master the art of research and trading.
I always advise you to do the proper back-tests and understand any risks relating to trading. For example, the above results are not indicative as the spread we have used is very competitive and may be considered hard to constantly obtain in the retail trading world (but not impossible). However, with institutional bid/ask spreads, it may be possible to lower the costs such as that a systematic medium-frequency strategy starts being very profitable. You must never expect guaranteed returns and it is your sole responsibility to generate your own signals.