How to use TSA for Logical Investments: Part 1.1 Sector Analysis

Dr. Vipul Joshi
10 min readApr 29, 2024

--

Objective

In the diverse landscape of the Indian stock market, there are a total of 5,309 listed companies on BSE. Based on the primary nature of the business area, all the stocks are typically classified into one or more sectors.

This classification helps investors and analysts to systematically analyze and compare companies within the same business area, facilitating easier decision-making based on sector-specific trends, performance, and economic factors. By studying the statistics of different sectors, we can discern patterns, strengths, and weaknesses within each sector.
Therefore, analyzing the performance of various Sectors is crucial as it helps in identifying high-performing sectors, enabling investors to target their investments more effectively.

In this Lab, we are going to demonstrate how we can use TSA to perform sector analysis which can help you make data-driven investment decisions. The Lab serves as an introductory session, designed to provide you with the fundamental tools and concepts needed for sector analysis. As you progress, we will introduce more advanced techniques and deeper insights, equipping you with the skills to navigate and excel in the complex environment of stock market investment.

Quarterly Sector Performance Analysis

In this section of the Lab, we are going to :

  1. Download historical data of the following sectors using yfinance at 1d interval.
^NSE: Nifty 50 Index, which represents the performance of the top 50 companies listed on the National Stock Exchange of India.
^CNXAUTO: Nifty Auto Index, tracking the performance of the automobile sector.
^NSEBANK: Nifty Bank Index, reflecting the performance of the banking sector.
NIFTY_FIN_SERVICE.NS: Nifty Financial Services Index, focusing on the financial services sector.
^CNXFMCG: Nifty FMCG Index, representing the fast-moving consumer goods sector.
^CNXIT: Nifty IT Index, tracking the information technology sector.
^CNXMEDIA: Nifty Media Index, reflecting the performance of the media sector.
^CNXMETAL: Nifty Metal Index, focusing on the metal and mining sector.
^CNXREALTY: Nifty Realty Index, representing the real estate sector.
^CNXCONSUM: Nifty Consumption Index, tracking the consumer goods sector.
^CNXENERGY: Nifty Energy Index, reflecting the performance of the energy sector.
^CNXINFRA: Nifty Infrastructure Index, focusing on the infrastructure sector.
^CNXPSUBANK: Nifty PSU Bank Index, representing the performance of public sector banks.
^CNXPSE: Nifty PSE Index, tracking the performance of public sector enterprises.
^CNXMNC: Nifty MNC Index, focusing on multinational corporations listed on the NSE.

2. Once we have the historical data, we are going to resample the data to “Quartely data” and “Year Wise Quarterly Data”.

3. We will then plot the Quarterly data and find the best and worst performers for each quarter of the year.

4. Finally, we will discuss some interesting observations and attempt to understand the possible reasons for getting such results!

Download the Data

import yfinance as yf
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

def download_data(ticker, start_date, end_date, period = '1d'):
"""
Download historical stock data from Yahoo Finance.

Parameters:
ticker (str): Ticker symbol of the stock.
start_date (str): Start date of the historical data (YYYY-MM-DD).
end_date (str): End date of the historical data (YYYY-MM-DD).
period (str): Interval for data (default is '1d' for daily).

Returns:
pandas.DataFrame: Historical stock data.
"""
try:
data = yf.download(ticker, start= start_date, end = end_date, period = period)
return data
except Exception as e:
print(f"Failed to download {ticker}: {e}")
return pd.DataFrame() # Return an empty DataFrame on failure

# Function to calculate quarterly returns
def calculate_quarterly_returns(data):
"""
Calculate quarterly returns based on Open and Close prices.

Parameters:
data (pandas.DataFrame): Historical stock data.

Returns:
pandas.Series: Quarterly returns.
"""

quarterly_resampled = data[['Open', 'Close']].resample('Q').ohlc().to_period('Q')
quarterly_resampled.index = quarterly_resampled.index.map(lambda x: x.strftime('Q%q'))
quarter_returns = 100 * (quarterly_resampled['Close']['close'] - quarterly_resampled['Open']['open']) / quarterly_resampled['Open']['open']
return quarter_returns.dropna()

# Function to calculate quarterly returns
def calculate_yearwise_quarter_returns(data):
"""
Calculate year-wise quarterly returns based on Open and Close prices.

Parameters:
data (pandas.DataFrame): Historical stock data.

Returns:
pandas.Series: Quarterly returns.
"""
quarterly_resampled = data[['Open', 'Close']].resample('Q').ohlc().to_period('Q')
quarter_returns = 100 * (quarterly_resampled['Close']['close'] - quarterly_resampled['Open']['open']) / quarterly_resampled['Open']['open']
return quarter_returns.dropna()

# Set the aesthetics for the plots
sns.set(style="whitegrid")

nifty_indices_tickers = ["^NSEI", "^CNXAUTO", "^NSEBANK", "NIFTY_FIN_SERVICE.NS", "^CNXFMCG", "^CNXIT", "^CNXMEDIA", "^CNXMETAL", "^CNXREALTY", "^CNXCONSUM", "^CNXENERGY", "^CNXINFRA", "^CNXPSUBANK", "^CNXPSE", "^CNXMNC"]

Example

Let’s use the function to download data for “^CNXMNC”

ticker_name =  "^CNXMNC"
ohlc_data = download_data(ticker = ticker_name, start_date = "2000-01-01", end_date = "2024-03-31", period = "1d")
# the data contains the Open, High, Low, Close prices for the index.
# Since it's an index, it is not actually traded on the stock market. Thus it's Volume is always 0 and is insignificant.
# Open : At what price did the index open on the day
# High : The highest price value for that day
# Low : The lowest price value for that day
# Close: the closing price of that day
# Adj Close : Adjusted Close is the price value after adjusting the price for corporate events like splits, bonus etc.
data.head()
           Open         High         Low       Close       Adj Close   Volume
Date
2011-01-31 4806.149902 4882.450195 4766.350098 4870.700195 4870.700195 0
2011-02-01 4895.899902 4910.549805 4800.899902 4812.649902 4812.649902 0
2011-02-02 4837.700195 4857.000000 4798.200195 4808.100098 4808.100098 0
2011-02-03 4791.350098 4848.299805 4788.250000 4830.649902 4830.649902 0
2011-02-04 4836.200195 4836.299805 4726.299805 4745.399902 4745.399902 0

Visualise the data

Let’s use the mplfinance library (a well known library for visualising financial data) to visualise the OHLC data obtained for ^CNXMNC (MNC sector).

# visualise the data
!pip install mplfinance

import mplfinance as mpf
# Define custom color scheme
colors = mpf.make_marketcolors(
up='g', # Green candles for upward price movement
down='r', # Red candles for downward price movement
edge='inherit', # Inherit edge color from candle color
wick='inherit', # Inherit wick color from candle color
volume='inherit' # Inherit volume color from candle color
)
style = mpf.make_mpf_style(marketcolors=colors)

mpf.plot(ohlc_data[-100:], type='candle', style=style, figsize = (20,8) )

The above data is in a “daily” time-frame. We will resample it to a quarterly time-frame (Q1, Q2, Q3, Q4) and then aggregate data to find the performance of the sector across different sectors.

# let's now resample the index data into quarterly data
sector_quarterly_results = calculate_quarterly_returns(ohlc_data)
# let's now aggregate the quarterly data into [Q1, Q2, Q3, Q4] and see how does this sector perform quarter by quarter
plt.figure(figsize = (12, 8))
ticker_data = sector_quarterly_results
min_date = ohlc_data.index.min()
max_date = ohlc_data.index.max()
returns = calculate_quarterly_returns(ohlc_data)

if not returns.empty:
sns.barplot(x=returns.index, y=returns.values, order=['Q1', 'Q2', 'Q3', 'Q4'], label = f"Date Range {min_date} - {max_date}")
plt.ylabel("Returns(%)")
plt.xlabel("Quarter")
plt.title(ticker_name)
plt.show()
Statistics of ^CNXMNC aggregated on a Quarter to Quarter basis showing the distribution of returns in a Quarter and it’s volatility

From the above graph, we can make following observations : Nifty MNC Index has historically performed poorly in the first quarter of the year. The second quarter has historically yielded positive returns averaging over 4%. The third and fourth quarter have reasonable performance.

Let’s extend this study to all the indices.

# Fetch the data for all the sectors one by one (you can also fetch all of them at once) and save it in a dictionary
full_data = {}
for ticker in nifty_indices_tickers:
print(f"Downloading data for {ticker}")
data = download_data(ticker, start_date = "2000-01-01", end_date = "2024-03-31")
if data.isna().sum().sum() :
data.ffill(inplace = True) #if there is any missing data then forward fill the data
full_data[ticker] = data

# Calculate the quarterly returns for all the sectors and plot them
plt.figure()
fig, axs = plt.subplots(nrows=5, ncols=3, figsize=(18, 22), tight_layout=True)
axs = axs.flatten() # Flatten the axes array for easier indexing
current_ax = 0 # Start with the first subplot index

for i, ticker in enumerate(nifty_indices_tickers):
if ticker in full_data and not full_data[ticker].empty:
ticker_data = full_data[ticker]
min_date = ticker_data.index.min()
max_date = ticker_data.index.max()
returns = calculate_quarterly_returns(ticker_data)

if not returns.empty:
sns.barplot(x=returns.index, y=returns.values, order=['Q1', 'Q2', 'Q3', 'Q4'], ax=axs[current_ax], label = f"Date Range {min_date} - {max_date}")
axs[current_ax].set_title(ticker) # Optionally set the title of the subplot to the ticker name
current_ax += 1 # Move to the next subplot index
plt.legend()
plt.savefig("sector_analysis.png")
plt.show()
Statistics of all major Sectors in Indian Stock Market aggregated on a Quarter to Quarter basis showing the distribution of returns in a Quarter and it’s volatility

Observations

  • The median quarterly returns are generally positive across most sectors, which could indicate a period of overall market growth.
  • Sectors like Metals and Media show greater volatility, with wider spreads and outliers, which may suggest a higher risk for investments.
  • The financial services and Banking sectors seem to have a consistently positive performance.
  • The Automobile and IT sectors also show robust positive medians, but the Automobile sector demonstrates more variability in Q1.
  • The Energy sector shows the highest median in Q1, possibly indicating seasonal influences or specific sectoral events.

Best & Worst Performers

Let us now find the sector that performs best and the worst performers in each of Q1, Q2, Q3 and Q4.

We will also calculate the year-wise quarterly returns which will allow us to do Trend Analysis further.


quarterly_returns_df = pd.DataFrame()
year_quarter_returns = {}
# Calculate and store returns for all sectors
for ticker in nifty_indices_tickers:
data = full_data[ticker]
returns = calculate_quarterly_returns(data)
yearwise_quarter_returns = calculate_yearwise_quarter_returns(data)
year_quarter_returns[ticker] = yearwise_quarter_returns
quarterly_returns_df[ticker] = returns.groupby(by = 'Date').mean()
# Determine the best-performing sector each quarter
best_performers = quarterly_returns_df.idxmax(axis=1)

print("Best performers : ", best_performers)
Best performers :  Date
Q1 ^CNXENERGY
Q2 ^CNXAUTO
Q3 ^CNXIT
Q4 ^CNXPSUBANK
dtype: object
# Output the results
worst_performers = quarterly_returns_df.idxmin(axis=1)
print("Worst PErformers : ", worst_performers)
Worst PErformers :  Date
Q1 ^CNXMEDIA
Q2 ^CNXPSUBANK
Q3 ^CNXPSUBANK
Q4 ^CNXFMCG
dtype: object

Insights and Commentary

Sector Volatility:

There’s noticeable volatility among the sectors across different quarters. For instance, ^CNXPSUBANK is the best performer in Q4 but the worst in Q2 and Q3. This indicates significant fluctuations in performance, which could be due to external economic factors, policy changes, or sector-specific issues.

Sector Analysis:

  1. Energy (^CNXENERGY) performing well in Q1 could be attributed to seasonal demand variations or geopolitical factors affecting energy prices.
  2. Automotive (^CNXAUTO) leading in Q2 might reflect market recovery or new model releases typically timed around this period.
  3. IT (^CNXIT) excelling in Q3 could be driven by technology demand surges or significant earnings reports commonly released in this quarter.
  4. Public Sector Banking (^CNXPSUBANK) showing a turnaround to become the best in Q4 could be related to fiscal year-end financial maneuvers or government interventions.

Strategic Implications:

For investors or analysts, this information is crucial for making informed decisions about which sectors might offer the best opportunities for investment in specific quarters. The variability also suggests the importance of a diversified portfolio to mitigate risks associated with sector-specific downturns.

Predictive Opportunities:

Understanding these patterns could aid in predictive modeling for future returns based on historical trends, economic indicators, or other predictive factors that influence these sectors.

Let’s see how each Sector has done in a Quarter by Quarter basis :

Q1

# Its interesting to see that a significantly large number of sectors perform poorly (mean data) in Q1
quarterly_returns_df.loc['Q1'].sort_values(ascending = False).plot(kind = "bar", figsize = (15,4))

Q2

# Its refreshing to see that a significantly large number of sectors give good returns in Q2 and Auto sector followed by banking sector gives the best results
quarterly_returns_df.loc['Q2'].sort_values(ascending = False).plot(kind = "bar", figsize = (15,4))

Q3

# The Q3 performance is generally "satisfactory"
quarterly_returns_df.loc['Q3'].sort_values(ascending = False).plot(kind = "bar", figsize = (15,4))

Q4

# The Q4 performance is also generally "satisfactory" with PSU bank outperforming other sectors. COULD THIS BE BECAUSE OF OUTLIERS?
quarterly_returns_df.loc['Q4'].sort_values(ascending = False).plot(kind = "bar", figsize = (15,4))

Conclusion

As we wrap up this analysis, it becomes evident that systematic sector analysis using Time Series Analysis (TSA) provides deep insights into the fluctuations and characteristics of different market segments. From identifying the best and worst performers each quarter to understanding sector-specific drivers behind these performances, TSA is a powerful tool for investors aiming to optimize their investment strategies.

Next Steps

In future articles, we will expand upon these insights by employing more sophisticated techniques such as Fourier Transforms to study seasonal trends and patterns across these sectors. By breaking down complex patterns into simpler components, we aim to uncover underlying cycles and trends that might not be immediately apparent, offering investors a deeper understanding of the temporal dynamics at play.

Call to Action

Investors and market analysts are encouraged to incorporate TSA into their analysis toolkit, not only to recognize past patterns but also to better anticipate future sectoral movements. Whether you’re looking to hedge risks or capitalize on upcoming trends, staying informed through data-driven strategies is crucial in the dynamic landscape of stock markets.

By analyzing the data across different quarters and years, as demonstrated, investors can make more informed decisions, potentially leading to better portfolio performance. We look forward to exploring further and unveiling more advanced methodologies in our upcoming articles.

For now, let’s continue to leverage the power of data to navigate the complexities of the stock market more effectively. Stay tuned for the next part of this series, where we delve into trend analysis using advanced mathematical tools!

--

--