Discover more from All About Trading!
A Simple Yet Effective Contrarian Indicator.
Creating the Chande Momentum Oscillator in Python.
Momentum oscillators are numerous and their added-value ranges between very bad to acceptable. It is normal that we will not find something very predictive if we are just applying a lag over the price but we can find insights on the recent behavior of the market price. This article discusses one known oscillator called the Chande Momentum Oscillator.
I have just released a new book after the success of the previous book. It features advanced trend following indicators and strategies with a GitHub page dedicated to the continuously updated code. Also, this book features the original colors after having optimized for printing costs. If you feel that this interests you, feel free to visit the below Amazon link, or if you prefer to buy the PDF version, you could contact me on LinkedIn.
Fetching Historical OHLC Data
Let us start by fetching historical OHLC data before seeing how to code the indicator in Python. One of the most famous trading platforms in the retail community is the MetaTrader5 software. It is a powerful tool that comes with its own programming language and its huge online community support. It also offers the possibility to export its historical short-term and long-term FX data.
The first thing we need to do is to simply download the platform from the official website. Then, after creating the demo account, we are ready to import the library in Python that allows to import the OHLC data from MetaTrader5.
A library is a group of structured functions that can be imported into our Python interpreter from where we can call and use the ones we want.
The easiest way to install the MetaTrader5 library is to go to the Python prompt on our computer and type:
pip install MetaTrader5
This should install the library in our local Python. Now, we want to import it to the Python interpreter (such as Pycharm or SPYDER) so that we can use it. Let us actually import all the libraries we will be using for this:
import datetime # Date acquiring import pytz # Time zone management import pandas as pd # Mostly for Data frame manipulation import MetaTrader5 as mt5 # Importing OHLC data import matplotlib.pyplot as plt # Plotting charts import numpy as np # Mostly for array manipulation
Anything that comes after “as” is a shortcut. The plt shortcut is there so that each time we want to call a function from that library we do not have to type the full matplotlib.pyplot statement.
The official documentation for the Metatrader5 library can be found here.
The first thing we can do is to select which time frame we want to import. Let us suppose that there are only two time frames, the 30-minute and the hourly bars. We can therefore create variables that hold the statement to tell the MetaTrader5 library which time frame we want.
# Choosing the 30-minute time frame frame_M30 = mt5.TIMEFRAME_M30
# Choosing the hourly time frame frame_H1 = mt5.TIMEFRAME_H1
Then, by staying in the spirit of importing variables, we can define the variable that states what date is it now. This helps the algorithm know the stopping date of the import. We can do this by the simple line of code below.
# Defining the variable now to give out the current date now = datetime.datetime.now()
Note that these code snippets are better used chronologically, hence, I encourage you to copy them in order and then execute them one by one so that you understand the evolution of what you are doing. The below is a function that holds which assets we want. Generally, I use 10 or more but for simplicity, let us consider that there are only two currency pairs: EURUSD and USDCHF.
def asset_list(asset_set): if asset_set == 'FX':
assets = ['EURUSD', 'USDCHF']
Now, with the key function that gets us the OHLC data. The below establishes a connection to MetaTrader5, applies the current date, and extracts the needed data. Notice the arguments year, month, and day. These will be filled by us to select from when do we want the data to start. Note, I have inputted Europe/Paris as my time zone, you should use your time zone to get more accurate data.
def get_quotes(time_frame, year = 2005, month = 1, day = 1, asset = "EURUSD"): # Establish connection to MetaTrader 5 if not mt5.initialize(): print("initialize() failed, error code =", mt5.last_error()) quit() timezone = pytz.timezone("Europe/Paris") utc_from = datetime.datetime(year, month, day, tzinfo = timezone) utc_to = datetime.datetime(now.year, now.month, now.day + 1, tzinfo = timezone) rates = mt5.copy_rates_range(asset, time_frame, utc_from, utc_to) rates_frame = pd.DataFrame(rates)
And finally, the last function we will use is the one that uses the below get_quotes function and then cleans the results so that we have a nice array. We have selected data since January 2019 as shown below.
def mass_import(asset, horizon): if horizon == 'M30': data = get_quotes(frame_M30, 2019, 1, 1, asset = assets[asset]) data = data.iloc[:, 1:5].values data = data.round(decimals = 5)
Finally, we are done building the blocks necessary to import the data. To import EURUSD OHLC historical data, we simply use the below code line:
# Choosing the horizon horizon = 'M30'
# Creating an array called EURUSD having M30 data since 2019 EURUSD = mass_import(0, horizon)
And voila, now we have the EURUSD OHLC data from 2019.
Creating the Chande Momentum Oscillator Step-By-Step
The Chande momentum oscillator is as its name describes it a technical indicator that uses the difference between recent highs and lows divided by their sum in order to gauge the strength of the trend. It is used to measure the relative strength in a market.
If we want to calculate the indicator in Python, we can follow these intuitive steps:
Calculate the sum of the closes that have closed higher during a period.
Calculate the sum of the closes that have closed lower during a period.
Subtract the result of the sum of the lower closes from the sum of the higher closes.
Divide the result from the previous step by the sum of the lower closes and higher closes.
Multiply the result by 100.
In less complicated terms, we will be allocating two columns at first where the first column is populated by the difference between the current closing price and the previous one if the current closing price is higher. The second column is populated by the difference between the current closing price and the previous one if the current closing price is lower.
Next, we can allocate another two columns where the first one will be populated by the sum of the values from the first column using a certain lookback period. The second column will be populated by the absolute sum of the values from the second column using a certain lookback period. Finally, we will allocate one last column which will apply the formula presented above giving us the Chande momentum oscillator.
This is of course not optimal at all code-wise but the aim here is to simplify the steps as much as possible while getting an accurate result.
# The function to add a number of columns inside 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
# The function to delete a 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 # The function to delete a 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)
# Remember, OHLC is an abbreviation of Open, High, Low, and Close and it refers to the standard historical data file def chande_momentum_oscillator(Data, lookback, close, where): # Adding a few columns Data = adder(Data, 5) # Calculating the number of higher closes for i in range(len(Data)): if Data[i, close] > Data[i - 1, close]: Data[i, where] = Data[i, close] - Data[i - 1, close] # Calculating the number of lower closes for i in range(len(Data)): if Data[i, close] < Data[i - 1, close]: Data[i, where + 1] = abs(Data[i, close] - Data[i - 1, close]) # Calculating the sum of higher closes for i in range(len(Data)): Data[i, where + 2] = Data[i - lookback + 1:i + 1, where].sum() # Calculating the sum of lower closes for i in range(len(Data)): Data[i, where + 3] = Data[i - lookback + 1:i + 1, where + 1].sum() # Calculating the Chande Momentum Oscillator for i in range(len(Data)): Data[i, where + 4] = (Data[i, where + 2] - Data[i, where + 3]) / (Data[i, where + 2] + Data[i, where + 3]) * 100 # Cleaning Data = deleter(Data, 4, 4) return Data
Generally, the indicator has implied boundaries at 50 and -50 but this can be personalized to each market and according to the current regime. For example, we can follow the trend of a bearish market by lowering the the 50 upper barrier to 40 where we can add to our shorts whenever the market comes back around that area.
If you are also interested by more technical indicators and strategies, then my book might interest you:
Using the Chande Momentum Oscillator
We use the indicator the same way as we use the Relative Strength Index. We establish upper and lower boundaries such as 50/-50 or 65/-65 as shown in the below signal chart on the USDCHF.
A buy (Long) signal is generated whenever the indicator reaches -65 with the previous value above -65.
A sell (Short) signal is generated whenever the indicator reaches 65 with the previous value below 65.
Signal chart on the USDCHF.
# Indicator Parameters lookback = 13 upper_barrier = 65 lower_barrier = -65
def signal(Data, indicator, buy, sell):
Data = adder(Data, 10) for i in range(len(Data)): if Data[i, indicator] <= lower_barrier and Data[i - 1, indicator] > lower_barrier: Data[i, buy] = 1el
if Data[i, indicator] >= upper_barrier and Data[i - 1, indicator] < upper_barrier: Data[i, sell] = -1
my_data = chande_momentum_oscillator(my_data, lookback, 3, 4) my_data = signal(my_data, 4, 6, 7)
Remember to always do your back-tests. You should always believe that other people are wrong. My indicators and style of trading may work for me but maybe not for you.
I am a firm believer of not spoon-feeding. I have learnt by doing and not by copying. You should get the idea, the function, the intuition, the conditions of the strategy, and then elaborate (an even better) one yourself so that you back-test and improve it before deciding to take it live or to eliminate it. My choice of not providing specific Back-testing results should lead the reader to explore more herself the strategy and work on it more.
One Last Word
I have recently started an NFT collection that aims to support different humanitarian and medical causes. The Society of Light is a set of limited collectibles which will help make the world slightly better as each sale will see a percentage of it sent directly to the charity attributed to the avatar. As I always say, nothing better than a bullet list to outline the benefits of buying these NFT’s:
High-potential gain: By concentrating the remaining sales proceedings on marketing and promoting The Society of Light, I am aiming to maximize their value as much as possible in the secondary market. Remember that trading in the secondary market also means that a portion of royalties will be donated to the same charity.
Art collection and portfolio diversification: Having a collection of avatars that symbolize good deeds is truly satisfying. Investing does not need to only have selfish needs even though there is nothing wrong with investing to make money. But what about investing to make money, help others, and collect art?
Donating to your preferred cause(s): This is a flexible way of allocating different funds to your charities.
A free copy of my book in PDF: Any buyer of any NFT will receive a free copy of my latest book shown in the link of the article.