Analyzing Portfolio Performance using Python
Analyzing a portfolio of stocks using Python involves leveraging various libraries and techniques to understand the performance, risk, and composition of a collection of stocks. Python provides powerful tools like Pandas, NumPy, Matplotlib, and others that facilitate data analysis, visualization, and optimization, making it a popular choice for portfolio analysis.
For the analysis, we create a portfolio of 4 stocks, namely, Tech Mahindra, Aurobindo Pharma, TATA Consumer Products and Havells. The stocks have been chosen randomly from the different sectoral NIFTY indices like NIFTY FMCG, NIFTY Pharma, NIFTY IT and NIFTY Consumer Durables. The stock data for these stocks have been taken from Yahoo finance.
Before performing the analysis following steps were taken to prepare the data for analysis:
- The required python libraries like Numpy, Pandas, Matplotlib, and Seaborn were imported.
- The data was read into the dataframe with company name using read_csv method.
- The date column in the data obtained from Yahoo finance was changed to datetime for pandas library to recognize it as date and set as the index column.
- Names of the stock were stored in a list to be used further in the code
The code for the above steps is shown below.
The code above shows the python libraries that were imported for the analysis. It shows the dataframe of the individual company stock data thate were read using the read_csv() method. The list of stock_names was also created containing the names of the company. Using the pd.to_datetime() and .set_index(), the date column was made the index column for performing date related calculations.
The image above shows the part about getting information regarding the datatframe tech_m containing the stock data about Tech Mahindra. It shows that there are no null values in the data. Similar process is done for other dataframes as well to check null values and get a general idea about the data in the dataframe.
To analyze the trend of movement of the daily price, we will look at normalized return for each individual stock. Normalized returns are calculated by dividing the price on each day by the price on the first day for the timeframe under consideration. The calculation in Python is shown below.
The code above shows the calculation for normalized returns using a for loop to divide the adjusted close price for each day by the adjusted close price on the first day for each stock dataframe. The normalized returns for tech_m (Tech Mahindra) is shown containing the Normed return column for the time period. The normed return column are created for each stock dataframe for other comapny stock data as well.
Let us assume a hypothetical allocation of 30% in Tech Mahindra, 30% in Aurobindo Pharma, 20% in TATA Consumer Products, 20% in Havells. For each allocation, the Normed return will be multiplied with allocation to obtain a base number for using our total allocation for the portfolio to calculate individual allocation for each stock.
The code above shows the use of zip function to attach the allocation to the dataframe of that company stock having the data.
The code above shows the creation of new column called “Allocation” in each individual stock dataframe by multiplying the allocation to the normalized return. For example, it can be seen in the tech_m dataframe that a new “Allocation” column is created by multiplying 0.3 (or 30%) with the Normed return. This is done for other dataframes as well.
Suppose, we want to allocate a total of Rs. 1,00,000 to this portfolio. To find the individual position for each stock, we multiply the total amount (in our case, Rs. 1,00,000) by the allocation. This calculation in Python is shown below.
The code above shows the calculation of individual position for each stock using a for loop by multiplying the total allocated amount for the portfolio with allocation in a separate new column called “Position”. This is done for all the stocks. For example, tech_m dataframe can be seen having the “Position” column obtained by multiplying the values in the “Allocation” column for Tech Mahindra by the total allocated amount of Rs.1,00,000.
By now, we have created the postions for each stock. Let us now put together all the positions for each stock into a separated dataframe called “portfolio_val”, which is short for portfolio value using the pd.concat() function. The code is shown below.
The code above shows a separate dataframe called “portfolio_val” having the positions for each stock.
To obtain and analyze the total position for the portfolio, the individual positions can be added. This is shown in the code below.
The above code shows the “Total Position” column created by adding the individual positions. This will be used to analyze the performance of portfolio.
The code above shows the movement of our portfolio for the timeframe under consideration by using the “Total Position” column in the portfolio_val dataframe.
The above image shows the relative movement of the four stocks by using the normalized returns. Using normalized returns helps in understanding the movement in relation to each other. The graph starts at 1 for all the stocks. It can be seen that only Havells shows a downward trend for the majority of the period. This is helpful in comparing the movement of prices of assets at different price levels.
Let us look at the daily returns of the portfolio as shown in the code below.
The code above shows the daily returns for the portfolio in a separate column called “Daily Returns”. Daily returns is calculated using the daily percent change in the total portfolio value shown in the “Total Position” column. The first value is NaN (Not a Number) because there is no value before it.
Using the daily returns value, we can calculate the average annual returns by multiplying it by 250 (since there are 250 trading days in a year) and average annual volatility by multiplying the daily standard deviation of the returns by square root of 250. The calculation in Python is shown below.
It can be seen in the image above that the portfolio has an average annual return of around 15.86 % and average annual volatility of around 18.85 %. The distribution of daily returns can be seen below in the histogram visualization.
The visualization above shows the distribution of the daily returns of the portfolio with returns on the x-axis.
Let us also look at the cumulative return of the portfolio from the start of the time period till the end. This is calculated as shown in the code below.
The cumulative returns, as shown in the image above, is calculated by calculating the percent change from the start till the end of time period. The cumulative return for the portfolio is around 51 %.
By now, we have calculated the daily returns, average annual returns and average annual volatility. Let us now use this data to calulate Sharpe Ratio. The Sharpe ratio gives the return delivered per unit of risk taken.
Sharpe Ratio is computed by calculating the difference between the return of the investment or portfolio and the return that can be earned from a risk-free investment, divided by the standard deviation of the investment or portfolio. The yield of 10 year Government bonds is generally taken as the risk free return. The calculation is shown below.
The code above shows the calculation of the Sharpe ratio. The values of average annual return and average annual volatility calculated in the above steps is taken directly. Also, 7.5% yield on 10 year Indian Government bonds is taken as risk free return. The Sharpe ratio comes out to be around 0.44 or 44%. The higher the Sharpe Ratio, the greater the potential return on investment for each unit of risk taken. Generally, if the Sharpe ratio of the investment is more than 1, it is considered favourable, and if it is below 1, it is considered unfavourable.
For our allocation and choice of stocks, the Sharpe ratio is not favorable as it is below 1. We can either choose a different portfolio of stocks or a different allocation to the existing stocks to obtain a better Sharpe Ratio. Although, the Sharpe ratio should not be read in isolation, as it is a relative measure. We should also look at the standard deviation (SD) when comparing the Sharpe ratio.
Till now we have analyzed the performance of our portfolio using several metrics. Let us now compare the returns of our portfolio with returns of other assets as well. So next, we will compare and look at the correlation of our portfolio’s returns with returns of the NIFTY 50 index and the Gold price. The code below shows the data for the NIFTY 50 index and Gold being read into a dataframe.
The code above shows the data that is being read for further analysis. The data for Gold prices was taken from the Nippon India Gold ETF which reflects the price of Gold. The daily returns are also calculated for the individual assets using the percent change in daily prices.
First, let us look at the scatter plot between the returns of NIFTY 50 and our portfolio as shown below.
It can be seen in the code and visualization above that there is some correlation between the returns of NIFTY 50 index and our portfolio. To find out more details, we will perform a regression analysis on their returns to find out the beta and correlation value.
The code above shows the regression analysis between the returns of NIFTY 50 and our portfolio. First, the stats module is imported from the scipy library in Python. The stats.linregress() function from SciPy performs a simple linear regression analysis between the returns of both. The results of the regression is stored in regress_result variable.
The slope of the regression is 0.88, which is also known as the beta value. So, the beta value of our portfolio is 0.88. A beta of 1 indicates that the asset tends to move in line with the NIFTY 50 (or, the market). A beta greater than 1 signifies higher volatility (more sensitivity), while a beta less than 1 implies lower volatility (less sensitivity). Also, the rvalue is 0.69 meaning the correlation between the returns is around 69 %.
We perform the same operation as above but this time with returns of Gold as shown below.
It can be seen above that there is very little relation between the returns of Gold and our portfolio. We perform a regression analysis to find more details.
The slope of the regression is -0.019 and the rvalue is -0.011. It means that there movement is not well correlated and the returns of Gold has a very little negative effect on the returns of our portfolio. Similar analysis can be done for understanding relationship between our portfolio and other assets like Oil prices, S&P 500, Euro Stoxx 50, Nikkei 225, etc.
Additionally, we can also look at the Treynor ratio for our portfolio. The Treynor ratio is a risk-adjusted measure of portfolio performance that evaluates the excess return earned per unit of systematic risk, also known as beta. It is particularly useful in assessing the performance of an investment or portfolio in relation to its systematic risk (or market risk, in our case the NIFTY 50 index).
The code above shows the calculation for the Treynor ratio. It can be calculated by dividing the excess returns (in our case, the difference of return of our portfolio and 7.5 % risk free return from the yield of 10 year Government bond) by the beta of the portfolio, which is a measure of the portfolio’s sensitivity to market movements. The beta value has already been computed as the value of slope of the regression between returns for NIFTY 50 index and our portfolio.
With this, we come to the end of our analysis. We looked at various metrics, calculations and visualizations for analyzing our portfolio performance. However, in our analysis, we assumed a random allocation of 30%, 30%, 20%, and 20% in the four stocks. In another project, we will look at optimizing our portfolio using Monte Carlo simulation, Markowitz theory and optimization algorithms to come up with an optimum allocations for the same stocks.