Powerful Sentiment Strategies For Equity Trading — The Put Call Ratio.
Creating a Strategy on the S&P500 Using the Put-Call Ratio in Python.
Sentiment Analysis is a vast and promising field in data analytics and trading. It is a rapidly rising type of analysis that uses the current pulse and market feeling to detect what participants intend to do or what positions they are holding.
Imagine you are planning to go see a movie and you want to anticipate whether this movie will be good or not, therefore, you ask many of your friends — whom have already seen the movie — about their opinions. Assuming 75% said the movie was good, you will have a certain confidence level that you will like it because the sentiment around this movie was mostly good (ignoring tastes and preferences). The same analogy can be applied to the financial markets through many ways either quantitative or qualitative.
Sometimes, indicators will be classified as more than one type, meaning a technical indicator can also be a sentiment indicator (e.g. the On-Balance Volume). And the way we analyze can also be technical (e.g. drawing support and resistance lines) or quantitative (e.g. mean-reversion).
This article will discuss an the famous Put-Call Ratio and how is it used to predict the direction of the S&P500. For this study, we will use the signal quality as a judge.
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.
Introduction to Sentiment Analysis
Sentiment Analysis is a type of predictive analytics that deal with alternative data other than the basic OHLC price structure. It is usually based on subjective polls and scores but can also be based on more quantitative measures such as expected hedges through market strength or weakness. One known objective sentiment analysis indicator is the Commitment of Traders report.
This article will deal with an indicator called the Put-Call Ratio, a time series provided by CBOE on a daily basis. The data itself harbors extremely valuable information when it comes to equity indices and trading. We will see how to download historical data on the S&P500 as well as the Put-Call Ratio, then we will design a trading strategy and evaluate it through the signal quality metric.
Options & The Put-Call Ratio
A call option is the right to buy a certain asset in the future at a pre-determined price while a put option is the right to sell a certain asset in the future at a pre-determined price.
Hence, when you buy a call you get to buy something later and when you buy a put you get to sell something later. Every transaction has two sides, therefore, when you’re buying a call or a put, someone else is selling them to you. This brings two other positions that can be taken on options, selling calls and selling puts. The Put-Call indicator deals with the buyers of options and measures the number of put buyers divided by the number of call buyers. That gives us an idea on the sentiment of market participants around the specified equity (in our case it will be the US stock market).
A higher put/call ratio means that there are more put buyers (traders are betting on the asset going lower) and a lower put/call ratio signifies more call buyers (traders are betting on the rise of the asset). A known way of using this ratio in analyzing market sentiment is by evaluating the following scenarios:
A rising ratio signifies a bearish sentiment. Professionals “feel” that the market will go lower.
A falling ratio signifies a bullish sentiment. Professionals “feel” that the market will go up.
There are two types of ratios, the total Put-Call Ratio and the equity Put-Call Ratio. In this study we will stick to the equity one as it better reflects the speculative crowd.
Downloading the Data & Designing the Strategy
Unfortunately, the up-to-date data of the Put-Call Ratio is no longer published for free on the CBOE website for some reason. Therefore, we need to find other ways to download the data among them multiple sources and creating free trials to be able to get the data. I have managed to come up with the latest 1000 data with the S&P500’s values in parallel as of August 2021. If you wish for more information, you may have to pay third party data providers. Anyway, in this GitHub link, you can get some historical data which I have legally obtained.
The first part is to download the EQPCR.xlsx data to your computer and then go into the Python interpreter and switch the source to where the excel file is located. Now, we have to import it and structure it.
import pandas as pd import matplotlib.pyplot as plt import numpy as np# Defining Primordial Functions def adder(Data, times): for i in range(1, times + 1): new = np.zeros((len(Data), 1), dtype = float) Data = np.append(Data, new, axis = 1) return Data def deleter(Data, index, times): for i in range(1, times + 1): Data = np.delete(Data, index, axis = 1) return Data def jump(Data, jump): Data = Data[jump:, ] return Data def indicator_plot_double(Data, first_panel, second_panel, window = 250):fig, ax = plt.subplots(2, figsize = (10, 5)) ax.plot(Data[-window:, first_panel], color = 'black', linewidth = 1) ax.grid() ax.plot(Data[-window:, second_panel], color = 'brown', linewidth = 1) ax.grid() ax.legend()my_data = pd.read_excel('EQPCR.xlsx') my_data = np.array(my_data)
Now, we should have an array called my_data with both the S&P500’s and the Put-Call Ratio’s historical data.
Subjectively, we can place barriers at 0.80 and 0.35 as they look to bound the ratio. These will be our trading triggers. Surely, some hindsight bias exists here but the idea is not to formulate a past trading strategy, rather see if the barriers will continue working in the future.
Following the intuition, we can have the following trading rules:
Buy (Long) whenever the ratio reaches 0.80 or surpasses it.
Sell (Short) whenever the ratio reaches 0.35 or breaks it.
def signal(Data, pcr, buy, sell): Data = adder(Data, 2) for i in range(len(Data)): if Data[i, pcr] >= 0.85 and Data[i - 1, buy] == 0 and Data[i - 2, buy] == 0 and Data[i - 3, buy] == 0: Data[i, buy] = 1 elif Data[i, pcr] <= 0.35 and Data[i - 1, sell] == 0 and Data[i - 2, sell] == 0 and Data[i - 3, sell] == 0: Data[i, sell] = -1 return Data my_data = signal(my_data, 0, 2, 3)
def signal_chart_ohlc_color(Data, what_bull, window = 1000): Plottable = Data[-window:, ] fig, ax = plt.subplots(figsize = (10, 5)) plt.plot(Data[-window:, 1], color = 'black', linewidth = 1) for i in range(len(Plottable)): if Plottable[i, 2] == 1: x = i y = Plottable[i, 1] ax.annotate(' ', xy = (x, y), arrowprops = dict(width = 9, headlength = 11, headwidth = 11, facecolor = 'green', color = 'green')) elif Plottable[i, 3] == -1: x = i y = Plottable[i, 1] ax.annotate(' ', xy = (x, y), arrowprops = dict(width = 9, headlength = -11, headwidth = -11, facecolor = 'red', color = 'red')) ax.set_facecolor((0.95, 0.95, 0.95)) plt.legend() plt.grid() signal_chart_ohlc_color(my_data, 1, window = 1000)
The code above gives us the signals shown in the chart. As a buying-the-dips timing indicator, the Put-Call Ratio may require some tweaking to improve the signals. Let us evaluate this using one metric for simplicity, the signal quality.
The signal quality is a metric that resembles a fixed holding period strategy. It is simply the reaction of the market after a specified time period following the signal. Generally, when trading, we tend to use a variable period where we open the positions and close out when we get a signal on the other direction or when we get stopped out (either positively or negatively). Sometimes, we close out at random time periods. Therefore, the signal quality is a very simple measure that assumes a fixed holding period and then checks the market level at that time point to compare it with the entry level. In other words, it measures market timing by checking the reaction of the market.
# Choosing an example of 21 periods period = 21 def signal_quality(Data, closing, buy, sell, period, where): Data = adder(Data, 1) for i in range(len(Data)): try: if Data[i, buy] == 1: Data[i + period, where] = Data[i + period, closing] - Data[i, closing] if Data[i, sell] == -1: Data[i + period, where] = Data[i, closing] - Data[i + period, closing] except IndexError: pass return Data # Using 21 Periods as a Window of signal Quality Check my_data = signal_quality(my_data, 1, 2, 3, period, 4) positives = my_data[my_data[:, 4] > 0] negatives = my_data[my_data[:, 4] < 0] # Calculating Signal Quality signal_quality = len(positives) / (len(negatives) + len(positives)) print('Signal Quality = ', round(signal_quality * 100, 2), '%')
A signal quality of 52.94% means that on 100 trades, we tend to see in 53 of the cases a profitable result if we act on it, without taking into account transaction costs. Of course, perfection is the rarest word in finance and this technique can sometimes give false signals as any strategy can.
If you are also interested by more technical indicators and using Python to create strategies, then my best-selling book on Technical Indicators may interest you:
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.
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.