Hierachical Clustering for CSMOM¶

In this notebook, you will create long-short CSMOM portfolios based on hierarchical clustering and analyse their performance. This notebook is structered as follows:

  1. Read Data
  2. Filter the Stocks
  3. Create Clusters, Create Portfolios and Rebalance
  4. Performance Analysis
  5. Conclusion

Import Libraries¶

Let's start by importing the necessary libraries for data manipulation, implementation of hierarchical clustering and visualisation.

In [15]:
# Import necessary libraries for data manipulation
import pandas as pd  # For working with data in dataframe
import numpy as np   # For numerical operations
import os

# Import necessary functions for hierarchical clustering analysis
from scipy.cluster.hierarchy import linkage, dendrogram, fcluster

# Import library for plotting graphs
import matplotlib.pyplot as plt  # For creating visualisations
#plt.style.use('seaborn-v0_8-whitegrid')  # Set the style for the plots
plt.style.use('seaborn-v0_8-darkgrid')

import warnings  # For suppressing warning messages during execution
warnings.filterwarnings('ignore')  # Ignore any warning messages during execution
#
# Helper functions
import sys
sys.path.append(os.path.abspath("../.."))

#from data_modules.capstone_util_quantra import plot_and_display_metrics_csmom
from Support.advanced_momentum_1_1 import plot_and_display_metrics_csmom

Read Data¶

Import the price and volume data of S&P 500 constituents from the CSV files sp500_prices_2014-01-01_to_2024-01-01 , sp500_volumes_2014-01-01_to_2024-01-01 and store in the dataframes sp500_prices and sp500_volumes. This files can be found in the data_modules folder of the zip file at the end of this section.

In [16]:
# Reading S&P 500 price data from CSV file into 'sp500_prices' DataFrame, setting the first column as the index
#sp500_prices = pd.read_csv(
#    '../data_modules/sp500_prices_2014-01-01_to_2024-01-01.csv', index_col=0)

# Reading S&P 500 volume data from CSV file into 'sp500_volume' DataFrame, setting the first column as the index
#sp500_volume = pd.read_csv(
#    '../data_modules/sp500_volumes_2014-01-01_to_2024-01-01.csv', index_col=0)

# Displaying the first few rows of the sp500_prices DataFrame
#sp500_prices.head()
In [17]:
import pandas as pd
import yfinance as yf
import requests
from io import StringIO

# --- Get S&P 500 tickers ---
url = "https://en.wikipedia.org/wiki/List_of_S%26P_500_companies"

headers = {
    "User-Agent": "Mozilla/5.0"
}

response = requests.get(url, headers=headers)
response.raise_for_status()

sp500 = pd.read_html(StringIO(response.text), header=0)[0]

tickers = sp500["Symbol"].tolist()
tickers = [t.replace(".", "-") for t in tickers]

print("Antal tickers:", len(tickers))

# --- Download data ---
data = yf.download(tickers, start="2010-01-01", auto_adjust=True)

# ✅ IMPORTANT: DO NOT FLATTEN

# Extract correctly from MultiIndex
sp500_prices = data['Close'].copy()
sp500_volume = data['Volume'].copy()

# Save
sp500_prices.to_csv("sp500_prices.csv")
sp500_volume.to_csv("sp500_volume.csv")

#print("✅ Files saved:")
#print("- sp500_prices.csv")
#print("- sp500_volume.csv")
Antal tickers: 503
[*********************100%***********************]  503 of 503 completed

Filter the Stocks¶

Perform the following steps to filter the stocks based on the daily tunover of the last 90 days.

  1. Multiply price and volume over n periods
  2. Take an average or mean of the above product to get average daily turnover

Average daily turnover in the stock market refers to the total value of securities traded during a standard trading day, typically averaged over a specific period, such as a month or a year.

In [18]:
# Calculate the average turnover of each stock by multiplying prices with volume data of the last 90 days

average_turnover = (sp500_prices[:90] * sp500_volume[:90]
                    ).mean()

# Filter the top 100 stocks based on average turnover
filtered_stocks = average_turnover.sort_values(ascending=False).index[:150]

# Prices of filtered stocks
filtered_stocks_prices = sp500_prices[filtered_stocks]
filtered_stocks_prices.index = pd.to_datetime(filtered_stocks_prices.index)

# Print filtered_stocks
filtered_stocks
Out[18]:
Index(['AAPL', 'BAC', 'GOOG', 'GOOGL', 'GS', 'C', 'MSFT', 'JPM', 'AMZN', 'GE',
       ...
       'EXC', 'MAR', 'TT', 'AMT', 'TXT', 'ITW', 'ADM', 'NEE', 'ALL', 'LUV'],
      dtype='object', name='Ticker', length=150)

The following are the steps to perform sequentially to find the monthly returns of the assets.

  • Resample the daily data to monthly data using the resample method.
  • After resampling, select the data of the last trading day of the month by using the method last.
  • Calculate the monthly returns by calling the method pct_change
In [19]:
# Resample the filtered prices to monthly frequency and calculate the percentage change from the previous month
monthly_returns = filtered_stocks_prices.resample('1M').last().pct_change()

# Removing the first row to eliminate NaN value resulting from percentage change calculation
monthly_returns = monthly_returns[1:]

# Display the first five rows of the monthly returns dataframe
monthly_returns.head()
Out[19]:
Ticker AAPL BAC GOOG GOOGL GS C MSFT JPM AMZN GE ... EXC MAR TT AMT TXT ITW ADM NEE ALL LUV
Date
2010-02-28 0.065396 0.097497 -0.005925 -0.005925 0.053662 0.024096 0.022145 0.077812 -0.055897 0.004990 ... -0.039472 0.035076 -0.014914 0.004947 0.019969 0.044276 -0.015393 -0.038746 0.044103 0.110326
2010-03-31 0.148470 0.072080 0.076538 0.076538 0.091332 0.191176 0.021626 0.066238 0.146706 0.133250 ... 0.011779 0.162671 0.092761 -0.001172 0.066750 0.047296 -0.015667 0.042269 0.040449 0.051288
2010-04-30 0.111021 -0.001120 -0.073036 -0.073036 -0.149035 0.079013 0.042676 -0.047428 0.009796 0.036264 ... -0.005022 0.166243 0.060510 -0.042244 0.075836 0.078970 -0.033218 0.076971 0.011142 -0.003025
2010-05-31 -0.016125 -0.117219 -0.076222 -0.076222 -0.003989 -0.093821 -0.151394 -0.070455 -0.084902 -0.133086 ... -0.103447 -0.088981 0.008924 -0.006861 -0.095009 -0.091390 -0.090541 -0.040730 -0.056159 -0.056146
2010-06-30 -0.020827 -0.086448 -0.083768 -0.083768 -0.090046 -0.050506 -0.108140 -0.075038 -0.129125 -0.112443 ... -0.016321 -0.104933 -0.073792 0.097952 -0.178137 -0.104634 0.021765 -0.013353 -0.062031 -0.106541

5 rows × 150 columns

Here are the lookback and holding details of the cross-sectional momentum strategy.

  1. number of lookback months = 24 since the lookback period is two years
  2. Number of holding months = 1
In [20]:
# Define the number of months for lookback and holding periods
lookback_months = 24
holding_months = 1

Create an empty dataframe stock_monthly_returns to store the monthly returns of the stocks in the portfolio. This empty dataframe will be updated in a loop once a new portfolio is created.

In [21]:
# Create an empty dataframe to store monthly returns for each stock
stock_monthly_returns = pd.DataFrame()

Create Clusters, Create Portfolios and Rebalance¶

Create clusters based on the monthly returns data of 24 month's data to create CSMOM portfolios. Rebalance at the beginning of every month.

In [22]:
# Loop through each month after the lookback period
for i in range(lookback_months, len(monthly_returns)):

    # Select the subset of monthly returns for the current lookback period
    # Store the historical and holding monthly returns data and store in 'returns'
    returns = monthly_returns[i - lookback_months:i + 1]

    # Store the historical in 'trailing_returns'
    trailing_returns = returns[:lookback_months]

    # Extract the starting, ending, and holding months from the subset
    starting_month = str(returns.index[0])[:7]
    ending_month = str(returns.index[-2])[:7]
    #holding_month = str(returns.index[-1])[:7]
    holding_month = returns.index[-1]
    print('_______________________________________________________')
    print(
        f" The historical data used to create the clusters is from {starting_month} to {ending_month}")

    # Set returns data as the transposed scaled trailing returns
    returns_data = trailing_returns.T

    # Initialise the number of clusters and maximum number of stocks per cluster
    num_clusters = 1
    max_stocks_per_cluster = 10

    # Perform hierarchical clustering using 'ward' linkage method
    linkage_matrix = linkage(trailing_returns.T, method='ward')

    # Assign cluster labels to stocks, ensuring each cluster has at most 10 stocks
    clusters = fcluster(linkage_matrix, num_clusters, criterion='maxclust')

    # Assign the cluster labels to the original returns data
    returns_data['Cluster'] = clusters

    # Adjust clusters until each cluster meets the constraint
    while max(returns_data['Cluster'].value_counts()) > max_stocks_per_cluster:
        num_clusters += 1
        clusters = fcluster(linkage_matrix, num_clusters, criterion='maxclust')
        returns_data['Cluster'] = clusters

    # Define the minimum number of stocks in a cluster
    minimum_stocks_in_cluster = 2

    # Filter out clusters with fewer than the minimum number of stocks
    filtered_clusters = returns_data.groupby('Cluster').filter(
        lambda x: len(x) >= minimum_stocks_in_cluster)['Cluster'].unique()

    # Assign the filtered cluster labels to the original price data
    returns_data = returns_data[returns_data['Cluster'].isin(filtered_clusters)]

    # Calculate the returns for each cluster and sum across clusters
    cluster_returns = returns_data.groupby('Cluster').mean().sum(axis=1)

    # Identify stocks to go short and long based on cluster returns
    short = np.array(returns_data[returns_data.Cluster ==
                     cluster_returns.idxmin()].index)

    long = np.array(returns_data[returns_data.Cluster ==
                    cluster_returns.idxmax()].index)

    # Extract the returns for holding stocks in the current month
    hold_returns = returns.iloc[-1]

    # Calculate the average returns for the stocks to go long and short
    long_returns = hold_returns[long].mean()
    short_returns = -1 * hold_returns[short].mean()

    # Calculate the total portfolio returns
    portfolio_returns = long_returns + short_returns

    # Print the portfolio returns for the current month
    print(
        f" The portfolio_returns in the month {str(monthly_returns.index[i])[:7]} are {np.round(portfolio_returns, 2)} %")

    # Copy monthly returns data for further manipulation
    returns_monthly = monthly_returns.copy()


    # Select returns correctly (row = date, columns = stocks)
    monthly_portfolio_returns = returns_monthly.loc[
        holding_month, list(long) + list(short)
    ].copy()
    
    # ✅ Robust short adjustment
    monthly_portfolio_returns[
        monthly_portfolio_returns.index.isin(short)
    ] *= -1
    
    # Identify non-portfolio stocks
    columns_to_nan = list(set(returns_monthly.columns) -
                          set(monthly_portfolio_returns.index))
    
    # Set NaN only for this month
    returns_monthly.loc[holding_month, columns_to_nan] = np.nan
    
    # ✅ Replace deprecated append
    stock_monthly_returns = pd.concat([
        stock_monthly_returns,
        returns_monthly.loc[[holding_month]]
    ])
_______________________________________________________
 The historical data used to create the clusters is from 2010-02 to 2012-01
 The portfolio_returns in the month 2012-02 are -0.17 %
_______________________________________________________
 The historical data used to create the clusters is from 2010-03 to 2012-02
 The portfolio_returns in the month 2012-03 are -0.06 %
_______________________________________________________
 The historical data used to create the clusters is from 2010-04 to 2012-03
 The portfolio_returns in the month 2012-04 are -0.04 %
_______________________________________________________
 The historical data used to create the clusters is from 2010-05 to 2012-04
 The portfolio_returns in the month 2012-05 are 0.04 %
_______________________________________________________
 The historical data used to create the clusters is from 2010-06 to 2012-05
 The portfolio_returns in the month 2012-06 are 0.01 %
_______________________________________________________
 The historical data used to create the clusters is from 2010-07 to 2012-06
 The portfolio_returns in the month 2012-07 are -0.07 %
_______________________________________________________
 The historical data used to create the clusters is from 2010-08 to 2012-07
 The portfolio_returns in the month 2012-08 are -0.12 %
_______________________________________________________
 The historical data used to create the clusters is from 2010-09 to 2012-08
 The portfolio_returns in the month 2012-09 are -0.03 %
_______________________________________________________
 The historical data used to create the clusters is from 2010-10 to 2012-09
 The portfolio_returns in the month 2012-10 are -0.06 %
_______________________________________________________
 The historical data used to create the clusters is from 2010-11 to 2012-10
 The portfolio_returns in the month 2012-11 are 0.04 %
_______________________________________________________
 The historical data used to create the clusters is from 2010-12 to 2012-11
 The portfolio_returns in the month 2012-12 are -0.02 %
_______________________________________________________
 The historical data used to create the clusters is from 2011-01 to 2012-12
 The portfolio_returns in the month 2013-01 are -0.06 %
_______________________________________________________
 The historical data used to create the clusters is from 2011-02 to 2013-01
 The portfolio_returns in the month 2013-02 are 0.01 %
_______________________________________________________
 The historical data used to create the clusters is from 2011-03 to 2013-02
 The portfolio_returns in the month 2013-03 are 0.04 %
_______________________________________________________
 The historical data used to create the clusters is from 2011-04 to 2013-03
 The portfolio_returns in the month 2013-04 are 0.04 %
_______________________________________________________
 The historical data used to create the clusters is from 2011-05 to 2013-04
 The portfolio_returns in the month 2013-05 are -0.03 %
_______________________________________________________
 The historical data used to create the clusters is from 2011-06 to 2013-05
 The portfolio_returns in the month 2013-06 are 0.05 %
_______________________________________________________
 The historical data used to create the clusters is from 2011-07 to 2013-06
 The portfolio_returns in the month 2013-07 are 0.05 %
_______________________________________________________
 The historical data used to create the clusters is from 2011-08 to 2013-07
 The portfolio_returns in the month 2013-08 are -0.06 %
_______________________________________________________
 The historical data used to create the clusters is from 2011-09 to 2013-08
 The portfolio_returns in the month 2013-09 are 0.12 %
_______________________________________________________
 The historical data used to create the clusters is from 2011-10 to 2013-09
 The portfolio_returns in the month 2013-10 are -0.01 %
_______________________________________________________
 The historical data used to create the clusters is from 2011-11 to 2013-10
 The portfolio_returns in the month 2013-11 are -0.01 %
_______________________________________________________
 The historical data used to create the clusters is from 2011-12 to 2013-11
 The portfolio_returns in the month 2013-12 are 0.03 %
_______________________________________________________
 The historical data used to create the clusters is from 2012-01 to 2013-12
 The portfolio_returns in the month 2014-01 are 0.03 %
_______________________________________________________
 The historical data used to create the clusters is from 2012-02 to 2014-01
 The portfolio_returns in the month 2014-02 are 0.07 %
_______________________________________________________
 The historical data used to create the clusters is from 2012-03 to 2014-02
 The portfolio_returns in the month 2014-03 are -0.03 %
_______________________________________________________
 The historical data used to create the clusters is from 2012-04 to 2014-03
 The portfolio_returns in the month 2014-04 are -0.0 %
_______________________________________________________
 The historical data used to create the clusters is from 2012-05 to 2014-04
 The portfolio_returns in the month 2014-05 are -0.05 %
_______________________________________________________
 The historical data used to create the clusters is from 2012-06 to 2014-05
 The portfolio_returns in the month 2014-06 are -0.02 %
_______________________________________________________
 The historical data used to create the clusters is from 2012-07 to 2014-06
 The portfolio_returns in the month 2014-07 are -0.02 %
_______________________________________________________
 The historical data used to create the clusters is from 2012-08 to 2014-07
 The portfolio_returns in the month 2014-08 are 0.04 %
_______________________________________________________
 The historical data used to create the clusters is from 2012-09 to 2014-08
 The portfolio_returns in the month 2014-09 are 0.01 %
_______________________________________________________
 The historical data used to create the clusters is from 2012-10 to 2014-09
 The portfolio_returns in the month 2014-10 are 0.02 %
_______________________________________________________
 The historical data used to create the clusters is from 2012-11 to 2014-10
 The portfolio_returns in the month 2014-11 are -0.04 %
_______________________________________________________
 The historical data used to create the clusters is from 2012-12 to 2014-11
 The portfolio_returns in the month 2014-12 are 0.04 %
_______________________________________________________
 The historical data used to create the clusters is from 2013-01 to 2014-12
 The portfolio_returns in the month 2015-01 are 0.06 %
_______________________________________________________
 The historical data used to create the clusters is from 2013-02 to 2015-01
 The portfolio_returns in the month 2015-02 are 0.07 %
_______________________________________________________
 The historical data used to create the clusters is from 2013-03 to 2015-02
 The portfolio_returns in the month 2015-03 are 0.06 %
_______________________________________________________
 The historical data used to create the clusters is from 2013-04 to 2015-03
 The portfolio_returns in the month 2015-04 are -0.09 %
_______________________________________________________
 The historical data used to create the clusters is from 2013-05 to 2015-04
 The portfolio_returns in the month 2015-05 are 0.08 %
_______________________________________________________
 The historical data used to create the clusters is from 2013-06 to 2015-05
 The portfolio_returns in the month 2015-06 are 0.09 %
_______________________________________________________
 The historical data used to create the clusters is from 2013-07 to 2015-06
 The portfolio_returns in the month 2015-07 are 0.06 %
_______________________________________________________
 The historical data used to create the clusters is from 2013-08 to 2015-07
 The portfolio_returns in the month 2015-08 are 0.04 %
_______________________________________________________
 The historical data used to create the clusters is from 2013-09 to 2015-08
 The portfolio_returns in the month 2015-09 are 0.06 %
_______________________________________________________
 The historical data used to create the clusters is from 2013-10 to 2015-09
 The portfolio_returns in the month 2015-10 are 0.03 %
_______________________________________________________
 The historical data used to create the clusters is from 2013-11 to 2015-10
 The portfolio_returns in the month 2015-11 are -0.06 %
_______________________________________________________
 The historical data used to create the clusters is from 2013-12 to 2015-11
 The portfolio_returns in the month 2015-12 are 0.13 %
_______________________________________________________
 The historical data used to create the clusters is from 2014-01 to 2015-12
 The portfolio_returns in the month 2016-01 are 0.16 %
_______________________________________________________
 The historical data used to create the clusters is from 2014-02 to 2016-01
 The portfolio_returns in the month 2016-02 are 0.23 %
_______________________________________________________
 The historical data used to create the clusters is from 2014-03 to 2016-02
 The portfolio_returns in the month 2016-03 are -0.09 %
_______________________________________________________
 The historical data used to create the clusters is from 2014-04 to 2016-03
 The portfolio_returns in the month 2016-04 are -0.13 %
_______________________________________________________
 The historical data used to create the clusters is from 2014-05 to 2016-04
 The portfolio_returns in the month 2016-05 are 0.02 %
_______________________________________________________
 The historical data used to create the clusters is from 2014-06 to 2016-05
 The portfolio_returns in the month 2016-06 are -0.02 %
_______________________________________________________
 The historical data used to create the clusters is from 2014-07 to 2016-06
 The portfolio_returns in the month 2016-07 are 0.02 %
_______________________________________________________
 The historical data used to create the clusters is from 2014-08 to 2016-07
 The portfolio_returns in the month 2016-08 are -0.04 %
_______________________________________________________
 The historical data used to create the clusters is from 2014-09 to 2016-08
 The portfolio_returns in the month 2016-09 are -0.2 %
_______________________________________________________
 The historical data used to create the clusters is from 2014-10 to 2016-09
 The portfolio_returns in the month 2016-10 are -0.03 %
_______________________________________________________
 The historical data used to create the clusters is from 2014-11 to 2016-10
 The portfolio_returns in the month 2016-11 are -0.16 %
_______________________________________________________
 The historical data used to create the clusters is from 2014-12 to 2016-11
 The portfolio_returns in the month 2016-12 are 0.02 %
_______________________________________________________
 The historical data used to create the clusters is from 2015-01 to 2016-12
 The portfolio_returns in the month 2017-01 are 0.02 %
_______________________________________________________
 The historical data used to create the clusters is from 2015-02 to 2017-01
 The portfolio_returns in the month 2017-02 are 0.07 %
_______________________________________________________
 The historical data used to create the clusters is from 2015-03 to 2017-02
 The portfolio_returns in the month 2017-03 are 0.03 %
_______________________________________________________
 The historical data used to create the clusters is from 2015-04 to 2017-03
 The portfolio_returns in the month 2017-04 are 0.14 %
_______________________________________________________
 The historical data used to create the clusters is from 2015-05 to 2017-04
 The portfolio_returns in the month 2017-05 are 0.09 %
_______________________________________________________
 The historical data used to create the clusters is from 2015-06 to 2017-05
 The portfolio_returns in the month 2017-06 are -0.11 %
_______________________________________________________
 The historical data used to create the clusters is from 2015-07 to 2017-06
 The portfolio_returns in the month 2017-07 are 0.02 %
_______________________________________________________
 The historical data used to create the clusters is from 2015-08 to 2017-07
 The portfolio_returns in the month 2017-08 are -0.01 %
_______________________________________________________
 The historical data used to create the clusters is from 2015-09 to 2017-08
 The portfolio_returns in the month 2017-09 are -0.02 %
_______________________________________________________
 The historical data used to create the clusters is from 2015-10 to 2017-09
 The portfolio_returns in the month 2017-10 are 0.07 %
_______________________________________________________
 The historical data used to create the clusters is from 2015-11 to 2017-10
 The portfolio_returns in the month 2017-11 are -0.13 %
_______________________________________________________
 The historical data used to create the clusters is from 2015-12 to 2017-11
 The portfolio_returns in the month 2017-12 are -0.02 %
_______________________________________________________
 The historical data used to create the clusters is from 2016-01 to 2017-12
 The portfolio_returns in the month 2018-01 are 0.02 %
_______________________________________________________
 The historical data used to create the clusters is from 2016-02 to 2018-01
 The portfolio_returns in the month 2018-02 are 0.14 %
_______________________________________________________
 The historical data used to create the clusters is from 2016-03 to 2018-02
 The portfolio_returns in the month 2018-03 are 0.01 %
_______________________________________________________
 The historical data used to create the clusters is from 2016-04 to 2018-03
 The portfolio_returns in the month 2018-04 are -0.07 %
_______________________________________________________
 The historical data used to create the clusters is from 2016-05 to 2018-04
 The portfolio_returns in the month 2018-05 are 0.08 %
_______________________________________________________
 The historical data used to create the clusters is from 2016-06 to 2018-05
 The portfolio_returns in the month 2018-06 are 0.01 %
_______________________________________________________
 The historical data used to create the clusters is from 2016-07 to 2018-06
 The portfolio_returns in the month 2018-07 are -0.02 %
_______________________________________________________
 The historical data used to create the clusters is from 2016-08 to 2018-07
 The portfolio_returns in the month 2018-08 are -0.04 %
_______________________________________________________
 The historical data used to create the clusters is from 2016-09 to 2018-08
 The portfolio_returns in the month 2018-09 are 0.02 %
_______________________________________________________
 The historical data used to create the clusters is from 2016-10 to 2018-09
 The portfolio_returns in the month 2018-10 are 0.03 %
_______________________________________________________
 The historical data used to create the clusters is from 2016-11 to 2018-10
 The portfolio_returns in the month 2018-11 are 0.15 %
_______________________________________________________
 The historical data used to create the clusters is from 2016-12 to 2018-11
 The portfolio_returns in the month 2018-12 are 0.09 %
_______________________________________________________
 The historical data used to create the clusters is from 2017-01 to 2018-12
 The portfolio_returns in the month 2019-01 are -0.12 %
_______________________________________________________
 The historical data used to create the clusters is from 2017-02 to 2019-01
 The portfolio_returns in the month 2019-02 are 0.09 %
_______________________________________________________
 The historical data used to create the clusters is from 2017-03 to 2019-02
 The portfolio_returns in the month 2019-03 are 0.05 %
_______________________________________________________
 The historical data used to create the clusters is from 2017-04 to 2019-03
 The portfolio_returns in the month 2019-04 are 0.07 %
_______________________________________________________
 The historical data used to create the clusters is from 2017-05 to 2019-04
 The portfolio_returns in the month 2019-05 are 0.17 %
_______________________________________________________
 The historical data used to create the clusters is from 2017-06 to 2019-05
 The portfolio_returns in the month 2019-06 are -0.06 %
_______________________________________________________
 The historical data used to create the clusters is from 2017-07 to 2019-06
 The portfolio_returns in the month 2019-07 are 0.01 %
_______________________________________________________
 The historical data used to create the clusters is from 2017-08 to 2019-07
 The portfolio_returns in the month 2019-08 are 0.2 %
_______________________________________________________
 The historical data used to create the clusters is from 2017-09 to 2019-08
 The portfolio_returns in the month 2019-09 are -0.04 %
_______________________________________________________
 The historical data used to create the clusters is from 2017-10 to 2019-09
 The portfolio_returns in the month 2019-10 are 0.02 %
_______________________________________________________
 The historical data used to create the clusters is from 2017-11 to 2019-10
 The portfolio_returns in the month 2019-11 are -0.04 %
_______________________________________________________
 The historical data used to create the clusters is from 2017-12 to 2019-11
 The portfolio_returns in the month 2019-12 are -0.12 %
_______________________________________________________
 The historical data used to create the clusters is from 2018-01 to 2019-12
 The portfolio_returns in the month 2020-01 are 0.18 %
_______________________________________________________
 The historical data used to create the clusters is from 2018-02 to 2020-01
 The portfolio_returns in the month 2020-02 are 0.15 %
_______________________________________________________
 The historical data used to create the clusters is from 2018-03 to 2020-02
 The portfolio_returns in the month 2020-03 are 0.46 %
_______________________________________________________
 The historical data used to create the clusters is from 2018-04 to 2020-03
 The portfolio_returns in the month 2020-04 are -0.3 %
_______________________________________________________
 The historical data used to create the clusters is from 2018-05 to 2020-04
 The portfolio_returns in the month 2020-05 are 0.05 %
_______________________________________________________
 The historical data used to create the clusters is from 2018-06 to 2020-05
 The portfolio_returns in the month 2020-06 are -0.2 %
_______________________________________________________
 The historical data used to create the clusters is from 2018-07 to 2020-06
 The portfolio_returns in the month 2020-07 are 0.06 %
_______________________________________________________
 The historical data used to create the clusters is from 2018-08 to 2020-07
 The portfolio_returns in the month 2020-08 are 0.08 %
_______________________________________________________
 The historical data used to create the clusters is from 2018-09 to 2020-08
 The portfolio_returns in the month 2020-09 are 0.2 %
_______________________________________________________
 The historical data used to create the clusters is from 2018-10 to 2020-09
 The portfolio_returns in the month 2020-10 are 0.09 %
_______________________________________________________
 The historical data used to create the clusters is from 2018-11 to 2020-10
 The portfolio_returns in the month 2020-11 are -0.3 %
_______________________________________________________
 The historical data used to create the clusters is from 2018-12 to 2020-11
 The portfolio_returns in the month 2020-12 are 0.01 %
_______________________________________________________
 The historical data used to create the clusters is from 2019-01 to 2020-12
 The portfolio_returns in the month 2021-01 are 0.1 %
_______________________________________________________
 The historical data used to create the clusters is from 2019-02 to 2021-01
 The portfolio_returns in the month 2021-02 are -0.3 %
_______________________________________________________
 The historical data used to create the clusters is from 2019-03 to 2021-02
 The portfolio_returns in the month 2021-03 are 0.03 %
_______________________________________________________
 The historical data used to create the clusters is from 2019-04 to 2021-03
 The portfolio_returns in the month 2021-04 are 0.03 %
_______________________________________________________
 The historical data used to create the clusters is from 2019-05 to 2021-04
 The portfolio_returns in the month 2021-05 are -0.08 %
_______________________________________________________
 The historical data used to create the clusters is from 2019-06 to 2021-05
 The portfolio_returns in the month 2021-06 are 0.04 %
_______________________________________________________
 The historical data used to create the clusters is from 2019-07 to 2021-06
 The portfolio_returns in the month 2021-07 are 0.03 %
_______________________________________________________
 The historical data used to create the clusters is from 2019-08 to 2021-07
 The portfolio_returns in the month 2021-08 are 0.01 %
_______________________________________________________
 The historical data used to create the clusters is from 2019-09 to 2021-08
 The portfolio_returns in the month 2021-09 are -0.13 %
_______________________________________________________
 The historical data used to create the clusters is from 2019-10 to 2021-09
 The portfolio_returns in the month 2021-10 are 0.1 %
_______________________________________________________
 The historical data used to create the clusters is from 2019-11 to 2021-10
 The portfolio_returns in the month 2021-11 are 0.04 %
_______________________________________________________
 The historical data used to create the clusters is from 2019-12 to 2021-11
 The portfolio_returns in the month 2021-12 are -0.06 %
_______________________________________________________
 The historical data used to create the clusters is from 2020-01 to 2021-12
 The portfolio_returns in the month 2022-01 are -0.06 %
_______________________________________________________
 The historical data used to create the clusters is from 2020-02 to 2022-01
 The portfolio_returns in the month 2022-02 are -0.06 %
_______________________________________________________
 The historical data used to create the clusters is from 2020-03 to 2022-02
 The portfolio_returns in the month 2022-03 are 0.08 %
_______________________________________________________
 The historical data used to create the clusters is from 2020-04 to 2022-03
 The portfolio_returns in the month 2022-04 are 0.02 %
_______________________________________________________
 The historical data used to create the clusters is from 2020-05 to 2022-04
 The portfolio_returns in the month 2022-05 are 0.1 %
_______________________________________________________
 The historical data used to create the clusters is from 2020-06 to 2022-05
 The portfolio_returns in the month 2022-06 are -0.08 %
_______________________________________________________
 The historical data used to create the clusters is from 2020-07 to 2022-06
 The portfolio_returns in the month 2022-07 are -0.13 %
_______________________________________________________
 The historical data used to create the clusters is from 2020-08 to 2022-07
 The portfolio_returns in the month 2022-08 are 0.14 %
_______________________________________________________
 The historical data used to create the clusters is from 2020-09 to 2022-08
 The portfolio_returns in the month 2022-09 are 0.02 %
_______________________________________________________
 The historical data used to create the clusters is from 2020-10 to 2022-09
 The portfolio_returns in the month 2022-10 are 0.18 %
_______________________________________________________
 The historical data used to create the clusters is from 2020-11 to 2022-10
 The portfolio_returns in the month 2022-11 are -0.06 %
_______________________________________________________
 The historical data used to create the clusters is from 2020-12 to 2022-11
 The portfolio_returns in the month 2022-12 are 0.04 %
_______________________________________________________
 The historical data used to create the clusters is from 2021-01 to 2022-12
 The portfolio_returns in the month 2023-01 are -0.27 %
_______________________________________________________
 The historical data used to create the clusters is from 2021-02 to 2023-01
 The portfolio_returns in the month 2023-02 are -0.04 %
_______________________________________________________
 The historical data used to create the clusters is from 2021-03 to 2023-02
 The portfolio_returns in the month 2023-03 are -0.15 %
_______________________________________________________
 The historical data used to create the clusters is from 2021-04 to 2023-03
 The portfolio_returns in the month 2023-04 are 0.04 %
_______________________________________________________
 The historical data used to create the clusters is from 2021-05 to 2023-04
 The portfolio_returns in the month 2023-05 are -0.05 %
_______________________________________________________
 The historical data used to create the clusters is from 2021-06 to 2023-05
 The portfolio_returns in the month 2023-06 are -0.03 %
_______________________________________________________
 The historical data used to create the clusters is from 2021-07 to 2023-06
 The portfolio_returns in the month 2023-07 are 0.05 %
_______________________________________________________
 The historical data used to create the clusters is from 2021-08 to 2023-07
 The portfolio_returns in the month 2023-08 are 0.06 %
_______________________________________________________
 The historical data used to create the clusters is from 2021-09 to 2023-08
 The portfolio_returns in the month 2023-09 are 0.13 %
_______________________________________________________
 The historical data used to create the clusters is from 2021-10 to 2023-09
 The portfolio_returns in the month 2023-10 are -0.05 %
_______________________________________________________
 The historical data used to create the clusters is from 2021-11 to 2023-10
 The portfolio_returns in the month 2023-11 are -0.22 %
_______________________________________________________
 The historical data used to create the clusters is from 2021-12 to 2023-11
 The portfolio_returns in the month 2023-12 are -0.1 %
_______________________________________________________
 The historical data used to create the clusters is from 2022-01 to 2023-12
 The portfolio_returns in the month 2024-01 are -0.04 %
_______________________________________________________
 The historical data used to create the clusters is from 2022-02 to 2024-01
 The portfolio_returns in the month 2024-02 are 0.2 %
_______________________________________________________
 The historical data used to create the clusters is from 2022-03 to 2024-02
 The portfolio_returns in the month 2024-03 are -0.02 %
_______________________________________________________
 The historical data used to create the clusters is from 2022-04 to 2024-03
 The portfolio_returns in the month 2024-04 are 0.05 %
_______________________________________________________
 The historical data used to create the clusters is from 2022-05 to 2024-04
 The portfolio_returns in the month 2024-05 are -0.01 %
_______________________________________________________
 The historical data used to create the clusters is from 2022-06 to 2024-05
 The portfolio_returns in the month 2024-06 are 0.2 %
_______________________________________________________
 The historical data used to create the clusters is from 2022-07 to 2024-06
 The portfolio_returns in the month 2024-07 are -0.1 %
_______________________________________________________
 The historical data used to create the clusters is from 2022-08 to 2024-07
 The portfolio_returns in the month 2024-08 are -0.04 %
_______________________________________________________
 The historical data used to create the clusters is from 2022-09 to 2024-08
 The portfolio_returns in the month 2024-09 are 0.07 %
_______________________________________________________
 The historical data used to create the clusters is from 2022-10 to 2024-09
 The portfolio_returns in the month 2024-10 are 0.08 %
_______________________________________________________
 The historical data used to create the clusters is from 2022-11 to 2024-10
 The portfolio_returns in the month 2024-11 are 0.09 %
_______________________________________________________
 The historical data used to create the clusters is from 2022-12 to 2024-11
 The portfolio_returns in the month 2024-12 are -0.07 %
_______________________________________________________
 The historical data used to create the clusters is from 2023-01 to 2024-12
 The portfolio_returns in the month 2025-01 are 0.07 %
_______________________________________________________
 The historical data used to create the clusters is from 2023-02 to 2025-01
 The portfolio_returns in the month 2025-02 are -0.16 %
_______________________________________________________
 The historical data used to create the clusters is from 2023-03 to 2025-02
 The portfolio_returns in the month 2025-03 are -0.09 %
_______________________________________________________
 The historical data used to create the clusters is from 2023-04 to 2025-03
 The portfolio_returns in the month 2025-04 are 0.11 %
_______________________________________________________
 The historical data used to create the clusters is from 2023-05 to 2025-04
 The portfolio_returns in the month 2025-05 are 0.15 %
_______________________________________________________
 The historical data used to create the clusters is from 2023-06 to 2025-05
 The portfolio_returns in the month 2025-06 are -0.12 %
_______________________________________________________
 The historical data used to create the clusters is from 2023-07 to 2025-06
 The portfolio_returns in the month 2025-07 are 0.12 %
_______________________________________________________
 The historical data used to create the clusters is from 2023-08 to 2025-07
 The portfolio_returns in the month 2025-08 are -0.08 %
_______________________________________________________
 The historical data used to create the clusters is from 2023-09 to 2025-08
 The portfolio_returns in the month 2025-09 are 0.46 %
_______________________________________________________
 The historical data used to create the clusters is from 2023-10 to 2025-09
 The portfolio_returns in the month 2025-10 are 0.22 %
_______________________________________________________
 The historical data used to create the clusters is from 2023-11 to 2025-10
 The portfolio_returns in the month 2025-11 are -0.01 %
_______________________________________________________
 The historical data used to create the clusters is from 2023-12 to 2025-11
 The portfolio_returns in the month 2025-12 are 0.06 %
_______________________________________________________
 The historical data used to create the clusters is from 2024-01 to 2025-12
 The portfolio_returns in the month 2026-01 are 0.09 %
_______________________________________________________
 The historical data used to create the clusters is from 2024-02 to 2026-01
 The portfolio_returns in the month 2026-02 are 0.06 %
_______________________________________________________
 The historical data used to create the clusters is from 2024-03 to 2026-02
 The portfolio_returns in the month 2026-03 are 0.02 %
_______________________________________________________
 The historical data used to create the clusters is from 2024-04 to 2026-03
 The portfolio_returns in the month 2026-04 are 0.68 %
_______________________________________________________
 The historical data used to create the clusters is from 2024-05 to 2026-04
 The portfolio_returns in the month 2026-05 are 0.17 %

Performance Analysis¶

Pass the stock_monthly_returns data to the function plot_and_display_metrics_csmom to generate the performance metrics and related plots.

In [23]:
# Plot and display metrics for the given monthly returns of stocks in a portfolio
plot_and_display_metrics_csmom(stock_monthly_returns)
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
Performance Metrics:
Metric Value
0 Sharpe Ratio 0.8
1 Maximum Drawdown Date 2022-09-30
2 Maximum Drawdown Value -0.29

Conclusion¶

The CSMOM portfolio created based on clusters formed using monthly returns data of past 24 months and held for a month generated cumulative returns close to 10 times the initial capital with a Sharpe ratio of 0.8 and maximum drawdown of 29%.

In [ ]: