The Fibonacci Range - Detecting Market Regime in Python
Creating a Simple Fibonacci-Based Concept to Understand the Market's Current State
Fibonacci is known in the world of trading as a method to find support and resistance levels. Generally, traders use retracements and projections to predict market reactions. In this article, we will stay in the world of Fibonacci but see how we can understand whether the market is in a short-term bullish or a bearish regime using the sequence.
I have just published a new book after the success of my previous one “New Technical Indicators in Python”. It features a more complete description and addition of structured trading strategies with a GitHub page dedicated to the continuously updated code. If you feel that this interests you, feel free to visit the below link, or if you prefer to buy the PDF version, you could contact me on LinkedIn.
Fetching Historical OHLC Data
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 libary 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 inputed 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.
The Market’s Regime
The market’s regime is its current state and can be divided into:
Bullish trend: The market has a tendency to make higher highs meaning that the aggregate direction is upwards.
Sideways: The market has a tendency to to range while remaining within established zones.
Bearish trend: The market has a tendency to make lower lows meaning that the aggregate direction is downwards.
Many tools attempt to detect the trend and most of them do give the answer but we can not really say we can predict the next state. The best way to solve this issue is to assume that the current state will continue and trade any reactions, preferably in the direction of the trend. For example, if the EURUSD is above its moving average and shaping higher highs, then it makes sense to wait for dips before buying and assuming that the bullish state will continue, also known as a trend-following strategy.
The above chart shows the EURUSD’s hourly values where the market trended upwards for a while before reversing. Now, we will discuss a complex technical indicator called the SuperTrend and see how we can color code the trend for more objectivity.
The Fibonacci Sequence
Leonardo Bonacci also known as Leonardo Fibonacci (which is a nickname to say son of Bonacci), has created one of the most fascinating series in our universe using simple addition techniques while observing rabbit populations. Now, to be fair, there is some evidence that suggest Indian mathematicians knew this sequence beforehand, we will stick to the widely accepted fact that Fibonacci came up with the sequence (Although, knowing the bright scientific and mathematical history of Indian researchers, I would not be surprised to know that they were the ones who discovered it first).
Fibonacci numbers are simply obtained using the following simple formula for n > 2:
This gives us the following sequence that goes to infinity:
1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ….
The Fibonacci Range
Mainly created for short-term regime detection, the Fibonacci Range method revolves around the idea of using highs and lows combined with the Fibonacci sequence to understand the current momentum and to give an approximate idea of whether the market is trending up, down, or flat. Here is an example of a chart before we see the steps needed to create the method.
The above chart is a color-coded candlestick graph where the values in red imply an on-going bearish regime, the values in green imply an on-going bullish regime, and the values in black imply a flat or neutral on-going regime.
What we are trying to do is simply confirm our trading entries. For instance, if we have a bullish signal from our strategy, we can check the market’s regime and see if it is confirming our signal or not. Notably, we must have the following alignments:
When we have a bullish signal from our trading strategy, it is recommended to act on it only if we are in a bullish regime.
When we have a bearish signal from our trading strategy, it is recommended to act on it only if we are in a bearish regime.
Now, how do we create the Fibonacci Range? And how is it different from other market regime detection techniques like moving averages and the SuperTrend indicator? To answer the former question, we follow the below steps:
For a bullish regime bar, the current high must be greater than the low from two periods ago, three periods ago, five periods ago, eight periods ago, and thirteen periods ago.
For a bearish regime bar, the current low must be lower than the high from two periods ago, three periods ago, five periods ago, eight periods ago, and thirteen periods ago.
For a flat regime bar, any one of the conditions above must be unsatisfied.
And to answer the latter question, there is basically no big difference except for the fact that the Fibonacci Range uses pure price action rather than mean values (Moving averages) or volatility (SuperTrend). Theoretically, this should reduce lag but practically, it is hard to prove.
# 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 fibonacci_range(Data, high, low, where):
# Adding a few columns Data = adder(Data, 1)
for i in range(len(Data)): if Data[i, high] > Data[i - 2, low] and \ Data[i, high] > Data[i - 3, low] and \ Data[i, high] > Data[i - 5, low] and \ Data[i, high] > Data[i - 8, low] and \ Data[i, high] > Data[i - 13, low]: Data[i, where] = 1
elif Data[i, low] < Data[i - 2, high] and \ Data[i, low] < Data[i - 3, high] and \ Data[i, low] < Data[i - 5, high] and \ Data[i, low] < Data[i - 8, high] and \ Data[i, low] < Data[i - 13, high]: Data[i, where] = -1
else: Data[i, where] = 0 return Data
my_data = fibonacci_range(my_data, 1, 2, 4)
The above function inputs 1’s in the desired column in case we have a bullish regime bar and -1’s in case we have a bearish regime bar. Similarly, the value of zero is inputted when we are in a flat regime.
def ohlc_plot_candles(Data, window): Chosen = Data[-window:, ] for i in range(len(Chosen)): if Chosen[i, 4] > 0: plt.vlines(x = i, ymin = Chosen[i, 2], ymax = Chosen[i, 1], color = 'green', linewidth = 1) color_chosen = 'green' plt.vlines(x = i, ymin = Chosen[i, 0], ymax = Chosen[i, 3], color = color_chosen, linewidth = 1) if Chosen[i, 4] < 0: plt.vlines(x = i, ymin = Chosen[i, 2], ymax = Chosen[i, 1], color = 'red', linewidth = 1) color_chosen = 'red' plt.vlines(x = i, ymin = Chosen[i, 3], ymax = Chosen[i, 0], color = color_chosen, linewidth = 1)
if Chosen[i, 4] == 0: plt.vlines(x = i, ymin = Chosen[i, 2], ymax = Chosen[i, 1], color = 'black', linewidth = 1) color_chosen = 'black' plt.vlines(x = i, ymin = Chosen[i, 3], ymax = Chosen[i, 0], color = color_chosen, linewidth = 1) plt.grid()
The above chart shows the Fibonacci Range method applied on the hourly values of Bitcoin. Whenever we see a confluence of black bars, we can understand that we are mostly flat.
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.
To sum up, are the strategies I provide realistic? Yes, but only by optimizing the environment (robust algorithm, low costs, honest broker, proper risk management, and order management). Are the strategies provided only for the sole use of trading? No, it is to stimulate brainstorming and getting more trading ideas as we are all sick of hearing about an oversold RSI as a reason to go short or a resistance being surpassed as a reason to go long. I am trying to introduce a new field called Objective Technical Analysis where we use hard data to judge our techniques rather than rely on outdated classical methods.