[Python] Which are the best and worst months for Straits Times Index?

By The Boy Who Procrastinates - May 22, 2020

In the previous post, we have addressed one of the seasonal stock market anomalies, namely the strategy of "selling in May" and how it has fared for the Straits Times Index (STI)

From the data, it was established that the Singapore blue-chip benchmark typically turns in lackluster performance in May with an average return of -1.68% over the last 20 years. Within that time frame, the month of May was negative in 13 of those years. 

In the midst of determining the seasonal effect, it has occurred that if most investors were selling in May, one may be tempted to conclude, albeit fallaciously, that May could possibly be the worst month for the local index in a given year. 

Still, the question continues to linger in the inquisitive mind: Which are the best and worst months for the Straits Times Index? 

Similarly, the Python code used to analyze the underlying data will be shared below. This time, I will utilize the existing Python modules so as to adopt a more elegant approach towards the seasonality analysis. 

Data Extraction for STI

First and foremost, we should extract the daily STI price data from Yahoo Finance. The time frame is defined to be 20 years, starting from 2000 to 2019 and the data of interest is the daily adjusted closing price of STI.

Subsequently, we assign the variable, sti_returns to the data of STI daily returns.

from pandas_datareader import data as pdr
import pandas as pd
import yfinance as yf


# Indicate start and end date
start = '2000-01-01'
end   = '2020-01-01'

# Generate STI price data
sti = pdr.get_data_yahoo('^STI', start, end)['Adj Close']

# Calculate STI daily returns
sti_returns = sti.pct_change() 

Monthly Heatmap for STI

With the data for daily STI returns and the help of built-in module, we can generate a monthly heatmap for the local equity market. 

import quantstats as qs

# Generate Monthly Heatmap for STI

The output produced is the following heatmap of monthly returns which is colour-coded for easy interpretation. According to the colour scale on the right, the magnitude of positive and negative values corresponds to the intensity of green or red.

Based on preliminary observation, the month of May has seen extreme returns, ranging from -17% to 21%. October 2008 marks the steepest decline of -24%, a month after the collapse of Lehman Brothers, which triggered the global financial carnage. Both January and September seem to have experienced a few instances of steep decline in excess of 10%.

However, the overwhelming amount of data presented may tell you anything you want to see. So it is perhaps practical to identify metrics for comparison. 

Calculate the average STI Return by Month

To calculate the average STI return for each month, we can simply employ the use of built-in mean function. 

# Calculate the average returns for each month
sti_monthly_returns = qs.stats.monthly_returns(sti_returns).iloc[:,:-1]

Average STI Return by Month

(Click to enlarge)

Contrary to initial hypothesis, the average STI return for the month of May is only the second poorest, with August taking the wooden spoon at -2.51%. 

On the flip side, the months of July and April lead the pack at 2.28% and 2.24% returns respectively.

Strangely, the equity market seems to exhibit patterns of extreme swings in which months that have posted extraordinary returns were succeeded by those with abysmal equity performance such as the Apr-May and Jul-Aug pairs.

The January effect theorizes that stock prices typically rise in the first month of the year following a year-end sell-off for tax purposes. However, the extent of such calendar effect in the local bourse is hardly eye-catching with a mere 0.2% return based on 20-years data.

Even with a reputation for stock losses, the month of October has unexpectedly recorded an average return of 0.54%. Not too shabby for a month that has been associated with historical market crashes. 

(Click to enlarge)

Narrowing the time frame to the last 5 years, we can see that May and August were still the catastrophic duo, recording returns at -3.35% and -4.22% respectively.  

On the other hand, April has been going strong as a lucrative month, posting decent gain at 2.47%. Unfortunately, the same cannot be said for July, with returns shrinking to 0.33%.

Calculate the Volatility of STI Return by Month

Now, to paint a better statistical picture of the monthly returns, we could probably add the degree of variation for STI to the analysis. 

To calculate the standard deviation, we can simply import built-in statistics library for computation. 

import statistics 

# Run a loop and calculate the standard deviation of STI returns by month
sd = {}
for month in sti_monthly_returns:
    sd[month] = statistics.stdev([returns for returns in sti_monthly_returns[month]])

pd.DataFrame.from_dict(sd, orient='index',columns=["Standard Deviation"])

Volatility of STI Return by Month

(Click to enlarge)

Volatility is a statistical measure of the dispersion of returns relative to its average, taking the form of the standard deviation. Lower volatility would mean that the index trades in a narrow range and does not fluctuate considerably. Conversely, the standard deviation increases if there are big swings in either direction of the STI prices.

Therefore, the computation of standard deviation helps to shed light on the historical volatility of the local index and to quantify the level of uncertainty associated with the size of changes in returns.

From the chart, it is discernible that the months of January, May, September and October exhibit relatively higher magnitude of variability, which likely suggest that the price action in these months tend to be less predictable. 

However, it should be noted that the high value of standard deviation for some months could be contributed by instances of extreme swings, such as 21% gain in May 2009 and 24% loss in October 2008.

Calculate the Percentage of Years with Positive/Negative STI Returns by Month

To calculate the number of years with positive/negative STI returns by month, we can write a simple nested loop to do so. 

# Set 2 empty lists
positive_month = []
negative_month = []

# Run a loop and count the number of years with positive/negative STI returns by month
for month in sti_monthly_returns:
    positive_count = 0 
    negative_count = 0
    for returns in sti_monthly_returns[month]:
        if returns >= 0:
            positive_count += 1
            negative_count += 1

Percentage of Years with Positive/Negative STI Returns by Month

(Click to enlarge)

With the consistently poor stock performance and low volatility in August, it should come as no surprise that the overwhelmingly occurrence of negative returns sums up to 16 out of 20 years.

Similarly, dismal stock market data for May has put it at negative returns for 13 years within the aforementioned time frame. 

In contrast, STI has mostly climbed in the months of March, April and July with over 70% of years with positive returns.

Final Thought

At the end of it, we have managed to debunk some of the perceived market anomalies for the local index such as the January and October Effect as the data has shown to go against these theories.

From the results presented, it would appear that some months are indeed significantly better than others. 

The returns for April and July have generally remained strong for the past 20 years. Even in this tumultuous times of the coronavirus outbreak, the STI has staged a surprise rally with an amazing 5.76% return for April 2020.

But for some reasons, the returns in May and August seem to have historically remained at the bottom of the league. 

Nevertheless, It should be emphasized again that past historical data should not be construed as a guarantee for future results. It would be a dangerously misleading approach to assume that the market will always display congruous seasonal pattern, especially when we are witnessing an unprecedented stock market reaction to the COVID-19.

You may wish to check out the Programming tab for more programming related articles.

If you do not wish to miss out on any articles, you may consider following the facebook page for timely update. 

Disclaimer: Kindly note that this is not a sponsored post. The Boy who Procrastinates has compiled the information for his own reference, with the hope that it will benefit others as well. It should not be used or construed as an offer to sell, a solicitation of an offer to buy, a recommendation for any security or as professional financial investment advice.

  • Share:

You Might Also Like


  1. Thank you so much for this nice information. Hope so many people will get aware of this and useful as well. And please keep update like this.

    Text Analytics Software

    Text Analytics Solutions