├── Lesson1_FundaRiskReturnPortfolio
├── DJIAMarketDataApr2016to20193YBeta.csv
├── DJIA_Apr112014_Apr112019.csv
├── DJIAkpf1Apr2016to20193YBeta.csv
├── Lesson1Eqn1_1.png
├── Lesson1Eqn1_2.png
├── Lesson1Eqn1_3.png
├── Lesson1Eqn1_4.png
├── Lesson1Eqn1_5.png
├── Lesson1Eqn1_6.png
├── Lesson1ExitTailImage.png
├── Lesson1Fig1_1.png
├── Lesson1Fig1_2.png
├── Lesson1Fig1_3.png
├── Lesson1GoalHeaderImage.png
└── Lesson1_MainContent.ipynb
├── Lesson2_FinDataWrangling
├── Lesson2ExitTailImage.png
├── Lesson2Fig2_1.png
├── Lesson2Fig2_2.png
├── Lesson2FinDataWranglingSampledata.csv
├── Lesson2GoalHeaderImage.png
├── Lesson2MissingValBSE200.csv
└── Lesson2_MainContent.ipynb
├── Lesson3_HeuristicPortfolioSelection
├── DJIA_Apr112014_Apr112019.csv
├── Lesson3Eqn3_1.png
├── Lesson3ExitTailImage.png
├── Lesson3Fig3_1.png
├── Lesson3Fig3_2.png
├── Lesson3Fig3_3.png
├── Lesson3Fig3_4.png
├── Lesson3Fig3_5.png
├── Lesson3GoalHeaderImage.png
└── Lesson3_MainContent.ipynb
├── Lesson4_TraditionalPortfolioConstruction
├── DJIA_Apr112014_Apr112019.csv
├── DJIA_Apr112014_Apr112019_kpf1.csv
├── Lesson4Eqn4_1.png
├── Lesson4Eqn4_2.png
├── Lesson4ExitTailImage.png
├── Lesson4GoalHeaderImage.png
└── Lesson4_MainContent.ipynb
├── Lesson5_MeanVarianceOptimization
├── DJIA_Apr112014_Apr112019.csv
├── DJIA_Apr112014_Apr112019_kpf1.csv
├── DJIA_Apr112014_Apr112019_kpf2.csv
├── DJIA_Apr112014_Apr112019_kpf3.csv
├── DJIAkpfsIdeal_EfficientFrontiersComparison.png
├── Lesson5Eqn5_1.png
├── Lesson5Eqn5_2.png
├── Lesson5Eqn5_3.png
├── Lesson5Eqn5_4.png
├── Lesson5Eqn5_5.png
├── Lesson5Eqn5_6.png
├── Lesson5ExitTailImage.png
├── Lesson5Fig5_1.png
├── Lesson5Fig5_2.png
├── Lesson5GoalHeaderImage.png
└── Lesson5_MainContent.ipynb
├── Lesson6_SharpeRatioOptimization
├── DJIA_Apr112014_Apr112019_kpf1.csv
├── Lesson6Eqn6_1.png
├── Lesson6Eqn6_2.png
├── Lesson6Eqn6_3.png
├── Lesson6Eqn6_4.png
├── Lesson6ExitTailImage.png
├── Lesson6GoalHeaderImage.png
└── Lesson6_MainContent.ipynb
├── Lesson7_ConstrainedPortfolioOptimization
├── DJIAMarketDataApr2016to20193YBeta.csv
├── DJIA_Apr112014_Apr112019_kpf1.csv
├── DJIAkpf1Apr2016to20193YBeta.csv
├── Lesson7Eqn7_1.png
├── Lesson7Eqn7_2.png
├── Lesson7Eqn7_3.png
├── Lesson7ExitTailImage.png
├── Lesson7GoalHeaderImage.png
└── Lesson7_MainContent.ipynb
└── README.md
/Lesson1_FundaRiskReturnPortfolio/Lesson1Eqn1_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaiViji/PythonFinance-PortfolioOptimization/2bb0ae9f32f44ff66302ea33f53c02e746583a05/Lesson1_FundaRiskReturnPortfolio/Lesson1Eqn1_1.png
--------------------------------------------------------------------------------
/Lesson1_FundaRiskReturnPortfolio/Lesson1Eqn1_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaiViji/PythonFinance-PortfolioOptimization/2bb0ae9f32f44ff66302ea33f53c02e746583a05/Lesson1_FundaRiskReturnPortfolio/Lesson1Eqn1_2.png
--------------------------------------------------------------------------------
/Lesson1_FundaRiskReturnPortfolio/Lesson1Eqn1_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaiViji/PythonFinance-PortfolioOptimization/2bb0ae9f32f44ff66302ea33f53c02e746583a05/Lesson1_FundaRiskReturnPortfolio/Lesson1Eqn1_3.png
--------------------------------------------------------------------------------
/Lesson1_FundaRiskReturnPortfolio/Lesson1Eqn1_4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaiViji/PythonFinance-PortfolioOptimization/2bb0ae9f32f44ff66302ea33f53c02e746583a05/Lesson1_FundaRiskReturnPortfolio/Lesson1Eqn1_4.png
--------------------------------------------------------------------------------
/Lesson1_FundaRiskReturnPortfolio/Lesson1Eqn1_5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaiViji/PythonFinance-PortfolioOptimization/2bb0ae9f32f44ff66302ea33f53c02e746583a05/Lesson1_FundaRiskReturnPortfolio/Lesson1Eqn1_5.png
--------------------------------------------------------------------------------
/Lesson1_FundaRiskReturnPortfolio/Lesson1Eqn1_6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaiViji/PythonFinance-PortfolioOptimization/2bb0ae9f32f44ff66302ea33f53c02e746583a05/Lesson1_FundaRiskReturnPortfolio/Lesson1Eqn1_6.png
--------------------------------------------------------------------------------
/Lesson1_FundaRiskReturnPortfolio/Lesson1ExitTailImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaiViji/PythonFinance-PortfolioOptimization/2bb0ae9f32f44ff66302ea33f53c02e746583a05/Lesson1_FundaRiskReturnPortfolio/Lesson1ExitTailImage.png
--------------------------------------------------------------------------------
/Lesson1_FundaRiskReturnPortfolio/Lesson1Fig1_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaiViji/PythonFinance-PortfolioOptimization/2bb0ae9f32f44ff66302ea33f53c02e746583a05/Lesson1_FundaRiskReturnPortfolio/Lesson1Fig1_1.png
--------------------------------------------------------------------------------
/Lesson1_FundaRiskReturnPortfolio/Lesson1Fig1_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaiViji/PythonFinance-PortfolioOptimization/2bb0ae9f32f44ff66302ea33f53c02e746583a05/Lesson1_FundaRiskReturnPortfolio/Lesson1Fig1_2.png
--------------------------------------------------------------------------------
/Lesson1_FundaRiskReturnPortfolio/Lesson1Fig1_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaiViji/PythonFinance-PortfolioOptimization/2bb0ae9f32f44ff66302ea33f53c02e746583a05/Lesson1_FundaRiskReturnPortfolio/Lesson1Fig1_3.png
--------------------------------------------------------------------------------
/Lesson1_FundaRiskReturnPortfolio/Lesson1GoalHeaderImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaiViji/PythonFinance-PortfolioOptimization/2bb0ae9f32f44ff66302ea33f53c02e746583a05/Lesson1_FundaRiskReturnPortfolio/Lesson1GoalHeaderImage.png
--------------------------------------------------------------------------------
/Lesson1_FundaRiskReturnPortfolio/Lesson1_MainContent.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "\n",
8 | "### Lesson 1\n"
9 | ]
10 | },
11 | {
12 | "cell_type": "markdown",
13 | "metadata": {},
14 | "source": [
15 | "\n",
16 | "# Fundamentals of Risk and Return of a Portfolio"
17 | ]
18 | },
19 | {
20 | "cell_type": "markdown",
21 | "metadata": {},
22 | "source": [
23 | ""
24 | ]
25 | },
26 | {
27 | "cell_type": "markdown",
28 | "metadata": {},
29 | "source": [
30 | "\n",
31 | "## 1.1 Introduction"
32 | ]
33 | },
34 | {
35 | "cell_type": "markdown",
36 | "metadata": {},
37 | "source": [
38 | "A **portfolio** is a basket of tradable assets such as stocks, bonds, commodities etc., which are held by an investor or investors. \n",
39 | "To keep matters simple and straight, let us consider an investor who is interested in investing in Dow stocks. Dow or Dow30 or Dow Jones Industrial Average (DJIA) is a stock market index that shows how a selective set of 30 large and publicly owned companies in the United States, have traded over a period of time. The investor is interested in the following Dow30 stocks, 29 in number and whose **ticker** symbols have been shown in parantheses:\n",
40 | "\n",
41 | "3M (MMM), American Express (AXP), Apple (AAPL), Boeing (BA), Caterpillar (CAT), Chevron (CVX), Cisco (CSCO), Coca-Cola (KO), Exxon Mobil (XOM), Goldman Sachs (GS), Home Depot (HD), IBM (IBM), Intel (INTC), Johnson & Johnson (JNJ), JPMorgan Chase (JPM), McDonald's (MCD), Merck (MRK), Microsoft (MSFT), Nike (NKE), Pfizer (PFE), Procter & Gamble (PG), Travelers Companies Inc. (TRV), United Health (UNH), United Technologies (UTX), Verizon (VZ), Visa (V), Walmart (WMT), Walgreens Boots Alliance (WBA) and Walt Disney (DIS).\n",
42 | "\n",
43 | "The daily historical prices of a few Dow30 stocks for the period April 01, 2019 to April 10, 2019 have been shown in Fig. 1.1, which is a snapshot of the CSV file, holding the historical dataset. The historical prices of all stocks traded in any global stock exchange can be downloaded for free from some financial websites or subscribed for, from stock exchanges or other websites. \n"
44 | ]
45 | },
46 | {
47 | "cell_type": "markdown",
48 | "metadata": {},
49 | "source": [
50 | "\n",
51 | "
Fig. 1.1 Historical prices (daily) of a few Dow30 stocks - a snapshot
"
52 | ]
53 | },
54 | {
55 | "cell_type": "markdown",
56 | "metadata": {},
57 | "source": [
58 | "## 1.2 Rate of Return"
59 | ]
60 | },
61 | {
62 | "cell_type": "markdown",
63 | "metadata": {},
64 | "source": [
65 | "Let us suppose the investor purchased a stock for a price $P_0$ at time $T_0$ and sold it at time $T_1$ for a price $P_1$. The time period between $T_0$ and $T_1$ is called as the **holding period** and the **rate of return** or the **holding period return** denoted by $R_T$ in the absence of any cash flows such as dividends etc., and expressed as a percentage is given by\n"
66 | ]
67 | },
68 | {
69 | "cell_type": "markdown",
70 | "metadata": {},
71 | "source": [
72 | "\n",
73 | "\n",
74 | "..........(1.1)
"
75 | ]
76 | },
77 | {
78 | "cell_type": "markdown",
79 | "metadata": {},
80 | "source": [
81 | "For example, the rate of return of AAPL stock assuming that it was bought on April 02, 2019 and sold on April 03, 2019 is, $ \\left(\\frac{195.35-194.02}{194.02}\\right)X 100 = 0.69\\%$. In other words, the daily return of AAPL is 0.69% for the day concerned. \n",
82 | " \n",
83 | "The holding period could be any calendar time such as days, weeks, months etc. \n",
84 | " \n",
85 | "The daily returns of all the Dow stocks listed in Section 1.1 can be computed using a Python code shown in the cell below.The code assumes that a CSV file with all the historical prices of the stocks, a snapshot of which was illustrated in Fig. 1.1 is available. The historical dataset considered for the Dow stocks is DJIA Index: April 11, 2014 to April 11, 2019. The CSV file comprises 1259 rows of data excluding the header and 29 columns excluding the date. The output of the Python code is the daily returns(%) of the stocks. "
86 | ]
87 | },
88 | {
89 | "cell_type": "code",
90 | "execution_count": 47,
91 | "metadata": {},
92 | "outputs": [
93 | {
94 | "name": "stdout",
95 | "output_type": "stream",
96 | "text": [
97 | "['AAPL', 'AXP', 'BA', 'CAT', 'CSCO', 'CVX', 'DIS', 'GS', 'HD', 'IBM', 'INTC', 'JNJ', 'JPM', 'KO', 'MCD', 'MMM', 'MRK', 'MSFT', 'NKE', 'PFE', 'PG', 'TRV', 'UNH', 'UTX', 'V', 'VZ', 'WBA', 'WMT', 'XOM']\n",
98 | "(1259, 29)\n",
99 | " AAPL AXP BA CAT CSCO CVX \\\n",
100 | "0 74.230003 84.540001 122.070000 101.449997 22.459999 117.029999 \n",
101 | "1 74.525711 85.500000 123.250000 102.779999 22.850000 118.699997 \n",
102 | "2 73.994286 86.040001 124.269997 102.500000 22.889999 120.300003 \n",
103 | "3 74.144287 87.400002 126.040001 102.930000 23.030001 121.830002 \n",
104 | "4 74.991432 86.220001 127.919998 102.830002 23.209999 123.680000 \n",
105 | "... ... ... ... ... ... ... \n",
106 | "1254 197.000000 110.959999 391.929993 140.360001 55.209999 126.419998 \n",
107 | "1255 200.100006 110.699997 374.519989 139.820007 55.490002 126.680000 \n",
108 | "1256 199.500000 109.849998 369.040009 136.350006 55.180000 125.540001 \n",
109 | "1257 200.619995 110.160004 364.940002 137.529999 55.820000 125.489998 \n",
110 | "1258 198.949997 109.849998 370.160004 138.869995 55.599998 125.989998 \n",
111 | "\n",
112 | " DIS GS HD IBM ... PFE \\\n",
113 | "0 77.010002 152.720001 75.699997 195.190002 ... 29.860001 \n",
114 | "1 77.620003 154.740005 75.970001 197.770004 ... 29.870001 \n",
115 | "2 77.660004 154.919998 75.889999 197.020004 ... 29.889999 \n",
116 | "3 78.949997 157.220001 76.580002 196.399994 ... 30.090000 \n",
117 | "4 79.989998 157.440002 77.089996 190.009995 ... 30.250000 \n",
118 | "... ... ... ... ... ... ... \n",
119 | "1254 115.000000 202.380005 202.059998 143.279999 ... 42.990002 \n",
120 | "1255 114.959999 202.539993 203.550003 143.389999 ... 43.139999 \n",
121 | "1256 116.860001 200.619995 200.899994 142.110001 ... 42.840000 \n",
122 | "1257 117.160004 202.979996 199.429993 143.020004 ... 42.730000 \n",
123 | "1258 116.599998 202.830002 201.479996 143.779999 ... 42.270000 \n",
124 | "\n",
125 | " PG TRV UNH UTX V VZ \\\n",
126 | "0 80.760002 85.300003 78.949997 113.930000 49.157501 47.070000 \n",
127 | "1 80.809998 85.500000 79.180000 114.940002 50.252499 47.270000 \n",
128 | "2 80.839996 85.889999 79.510002 115.839996 51.012501 46.919998 \n",
129 | "3 81.650002 86.779999 78.190002 118.070000 52.340000 47.099998 \n",
130 | "4 81.760002 86.680000 75.779999 118.570000 51.987499 47.599998 \n",
131 | "... ... ... ... ... ... ... \n",
132 | "1254 103.650002 136.399994 248.779999 133.720001 157.649994 59.090000 \n",
133 | "1255 104.970001 135.880005 248.750000 133.559998 157.750000 59.130001 \n",
134 | "1256 104.660004 135.190002 248.789993 132.250000 157.490005 58.400002 \n",
135 | "1257 104.650002 135.460007 246.029999 131.660004 158.559998 58.610001 \n",
136 | "1258 104.750000 136.279999 235.419998 132.820007 157.860001 58.560001 \n",
137 | "\n",
138 | " WBA WMT XOM \n",
139 | "0 64.260002 76.500000 96.720001 \n",
140 | "1 65.669998 77.379997 97.860001 \n",
141 | "2 66.010002 76.879997 98.680000 \n",
142 | "3 66.160004 77.220001 99.940002 \n",
143 | "4 66.750000 77.660004 100.419998 \n",
144 | "... ... ... ... \n",
145 | "1254 54.689999 98.830002 82.489998 \n",
146 | "1255 55.060001 99.230003 83.000000 \n",
147 | "1256 54.500000 98.690002 81.930000 \n",
148 | "1257 54.509998 99.599998 81.559998 \n",
149 | "1258 53.439999 100.800003 81.949997 \n",
150 | "\n",
151 | "[1259 rows x 29 columns]\n"
152 | ]
153 | }
154 | ],
155 | "source": [
156 | "#Python code to compute the daily returns in percentage, of Dow Stocks listed in Sec.1.1\n",
157 | "#calls function StockReturnsComputing to compute asset returns\n",
158 | "\n",
159 | "#dependencies\n",
160 | "import numpy as np\n",
161 | "import pandas as pd\n",
162 | "\n",
163 | "#input stock prices dataset\n",
164 | "stockFileName = 'DJIA_Apr112014_Apr112019.csv'\n",
165 | "rows = 1259 #excluding header\n",
166 | "columns = 29 #excluding date\n",
167 | "\n",
168 | "#read stock prices \n",
169 | "df = pd.read_csv(stockFileName, nrows= rows)\n",
170 | "\n",
171 | "#extract asset labels\n",
172 | "assetLabels = df.columns[1:columns+1].tolist()\n",
173 | "print(assetLabels)\n",
174 | "\n",
175 | "#extract asset prices data\n",
176 | "stockPrice = df.iloc[0:, 1:]\n",
177 | "print(stockPrice.shape)\n",
178 | "\n",
179 | "#print stock price\n",
180 | "print(stockPrice)\n"
181 | ]
182 | },
183 | {
184 | "cell_type": "markdown",
185 | "metadata": {},
186 | "source": [
187 | "The Python function **StockReturnsComputing (StockPrice, Rows, Columns)** computes the daily returns of stocks, given the daily stock prices **StockPrice** and its size (**Rows, Columns**)."
188 | ]
189 | },
190 | {
191 | "cell_type": "code",
192 | "execution_count": 48,
193 | "metadata": {},
194 | "outputs": [],
195 | "source": [
196 | "#function to compute asset returns \n",
197 | "def StockReturnsComputing(StockPrice, Rows, Columns):\n",
198 | " \n",
199 | " import numpy as np\n",
200 | " \n",
201 | " StockReturn = np.zeros([Rows-1, Columns])\n",
202 | " for j in range(Columns): # j: Assets\n",
203 | " for i in range(Rows-1): # i: Daily Prices\n",
204 | " StockReturn[i,j]=((StockPrice[i+1, j]-StockPrice[i,j])/StockPrice[i,j])* 100\n",
205 | "\n",
206 | " return StockReturn"
207 | ]
208 | },
209 | {
210 | "cell_type": "markdown",
211 | "metadata": {},
212 | "source": [
213 | "The Python code fragment shown in the cell below, computes the daily returns of the Dow stocks selected by the investor."
214 | ]
215 | },
216 | {
217 | "cell_type": "code",
218 | "execution_count": 49,
219 | "metadata": {},
220 | "outputs": [
221 | {
222 | "name": "stdout",
223 | "output_type": "stream",
224 | "text": [
225 | "Daily returns of selective Dow 30 stocks\n",
226 | " [[ 0.39836722 1.13555594 0.96665847 ... 2.19420472 1.15032288\n",
227 | " 1.17866004]\n",
228 | " [-0.71307606 0.63158012 0.82758377 ... 0.51774632 -0.64616182\n",
229 | " 0.83793071]\n",
230 | " [ 0.20271971 1.5806613 1.42432127 ... 0.22724132 0.44225288\n",
231 | " 1.27685651]\n",
232 | " ...\n",
233 | " [-0.29985306 -0.76784013 -1.46320094 ... -1.01707408 -0.54419126\n",
234 | " -1.28915663]\n",
235 | " [ 0.561401 0.28220847 -1.11099255 ... 0.01834495 0.92207517\n",
236 | " -0.45160747]\n",
237 | " [-0.83241852 -0.2814143 1.43037266 ... -1.96294082 1.20482432\n",
238 | " 0.47817436]]\n"
239 | ]
240 | }
241 | ],
242 | "source": [
243 | "#compute daily returns in percentage of the Dow stocks\n",
244 | "\n",
245 | "import numpy as np\n",
246 | "\n",
247 | "stockPriceArray = np.asarray(stockPrice)\n",
248 | "[Rows, Cols]=stockPriceArray.shape\n",
249 | "stockReturns = StockReturnsComputing(stockPriceArray, Rows, Cols)\n",
250 | "print('Daily returns of selective Dow 30 stocks\\n', stockReturns)\n"
251 | ]
252 | },
253 | {
254 | "cell_type": "markdown",
255 | "metadata": {},
256 | "source": [
257 | "## 1.3 Return on Market Index"
258 | ]
259 | },
260 | {
261 | "cell_type": "markdown",
262 | "metadata": {},
263 | "source": [
264 | "A **Market Index** serves to give a fair idea about how the overall market data or specific segments of the market perform. DJIA index is one of several indices that exist in global markets and attempts to describe market conditions with the help of the 30 companies listed under it. \n",
265 | " \n",
266 | "Market index data comprises the following details, viz., **Open**, **High**, **Low**, **Close**, **Adjusted Close** and **Volume**, indicating the opening, high, low, closing and adjusted close of the prices and the volume of trade undertaken for the period, respectively. Of these, the closing price of the market index is considered to be important. \n",
267 | " \n",
268 | "A snapshot of the DJIA Index for the period between April 01, 2019 to April 10, 2019 is shown in Fig. 1.2."
269 | ]
270 | },
271 | {
272 | "cell_type": "markdown",
273 | "metadata": {},
274 | "source": [
275 | "\n",
276 | "\n",
277 | "Fig. 1.2 Market index of DJIA during April 01, 2019 to April 10, 2019 - a snapshot
"
278 | ]
279 | },
280 | {
281 | "cell_type": "markdown",
282 | "metadata": {},
283 | "source": [
284 | "The **return on market index** is computed in a way similar to the rate of return of a stock explained by equation (1.1). Thus the daily return of market index is obtained by subtracting the closing price at time T with that at time (T-1) and dividing the difference by the closing price at time (T-1), before expressing it as a percentage. The Adjusted Closing price can also be considered for the computation, in lieu of Closing price. \n",
285 | "The DJIA market index percentage returns for the period April 02, 2019 to April 10, 2019 can be computed as, (-0.30, 0.15 0.64 0.15 -0.32 -0.72 0.03).\n"
286 | ]
287 | },
288 | {
289 | "cell_type": "markdown",
290 | "metadata": {},
291 | "source": [
292 | "Market indices are useful to investors since it helps investors to measure the performance of their portfolios against that of the markets. Thus if the daily market index return was 0.15 and that of the portfolio which the investor holds was 0.19 then there is a reason to rejoice, since the investor was able to beat the market!"
293 | ]
294 | },
295 | {
296 | "cell_type": "markdown",
297 | "metadata": {},
298 | "source": [
299 | "## 1.4 Mean and Covariance of Returns "
300 | ]
301 | },
302 | {
303 | "cell_type": "markdown",
304 | "metadata": {},
305 | "source": [
306 | "The mean and covariance of returns play a dominant role in determining the return and risk of a portfolio. The mean returns are denoted by $(\\mu_1, \\mu_2, ...\\mu_N)$ for the N stocks comprising the portfolio. The covariance of returns is a symmetric matrix referred to as the **variance-covariance matrix of returns** and is denoted by $\\left(\\sigma_{i,j}\\right)$. The diagonal of the matrix $\\left(\\sigma_{i,i}\\right)$ denotes the variance of returns. The Python code fragment demonstrating the computatation of the mean and covariance of returns is shown below: "
307 | ]
308 | },
309 | {
310 | "cell_type": "code",
311 | "execution_count": 50,
312 | "metadata": {},
313 | "outputs": [
314 | {
315 | "name": "stdout",
316 | "output_type": "stream",
317 | "text": [
318 | "Mean returns of Dow Stocks:\n",
319 | " [ 0.09027598 0.02910022 0.09966449 0.0386567 0.0809993 0.01551509\n",
320 | " 0.03975381 0.03313877 0.08479014 -0.01611022 0.07274221 0.03154811\n",
321 | " 0.06041058 0.01915697 0.05653419 0.04422144 0.03579986 0.09974359\n",
322 | " 0.08008644 0.03375828 0.02514231 0.04254659 0.09548076 0.01871582\n",
323 | " 0.10128134 0.02314321 -0.00168528 0.02946248 -0.0061016 ]\n",
324 | "Variance-covariance matrix of returns of Dow Stocks:\n",
325 | "\n",
326 | "[[2.37512857 0.67159954 0.96196299 1.04226142 0.99890235 0.66413311\n",
327 | " 0.68002487 0.9540385 0.72630968 0.70945859 1.07333017 0.48572094\n",
328 | " 0.8251006 0.30601011 0.45823283 0.73218 0.53433977 1.22660419\n",
329 | " 0.77524223 0.5444771 0.41917661 0.50943202 0.77433417 0.69274799\n",
330 | " 1.0086701 0.29354661 0.69735896 0.44587945 0.63272823]\n",
331 | " [0.67159954 1.64817768 0.80008198 0.9502813 0.70010508 0.63080612\n",
332 | " 0.56872139 1.06463549 0.65826615 0.66251941 0.73789772 0.47196466\n",
333 | " 1.00101545 0.30743029 0.35037656 0.65268493 0.55568077 0.7945144\n",
334 | " 0.68112631 0.54156063 0.34473927 0.53506318 0.71830933 0.67723734\n",
335 | " 0.84700893 0.34247718 0.66688086 0.37953128 0.52158702]\n",
336 | " [0.96196299 0.80008198 2.28788869 1.31010124 0.88951117 0.81235903\n",
337 | " 0.71613388 1.06553088 0.7474291 0.77691868 0.96595019 0.56720978\n",
338 | " 0.97737004 0.38099252 0.47170257 0.87223471 0.57833707 0.92506778\n",
339 | " 0.8218734 0.53242063 0.41390469 0.62206326 0.74452648 0.93342475\n",
340 | " 0.92483951 0.42031728 0.67937008 0.50859443 0.72979658]\n",
341 | " [1.04226142 0.9502813 1.31010124 2.73293774 1.04070962 1.23579738\n",
342 | " 0.68846207 1.32135157 0.79609702 0.88512353 1.15246617 0.53207219\n",
343 | " 1.16862013 0.3575928 0.45532119 1.06821468 0.61626811 1.10384461\n",
344 | " 0.78896131 0.60979315 0.39121348 0.67869145 0.72009107 0.98562753\n",
345 | " 0.96349618 0.400679 0.68112899 0.43156621 1.05224209]\n",
346 | " [0.99890235 0.70010508 0.88951117 1.04070962 1.78902556 0.77315447\n",
347 | " 0.71301493 0.92736842 0.72425245 0.81719807 1.15660938 0.58309596\n",
348 | " 0.90853687 0.36218917 0.47658137 0.80528362 0.64732853 1.14200595\n",
349 | " 0.78507677 0.62237188 0.44128741 0.58736908 0.65635776 0.73868178\n",
350 | " 0.9396166 0.45371174 0.70654156 0.56006952 0.70953946]\n",
351 | " [0.66413311 0.63080612 0.81235903 1.23579738 0.77315447 1.932096\n",
352 | " 0.57422635 0.92783679 0.59651661 0.67870537 0.80956635 0.50655966\n",
353 | " 0.92523497 0.38139009 0.43457364 0.6903186 0.58157564 0.80664559\n",
354 | " 0.54177056 0.50672642 0.42583641 0.5708276 0.58621616 0.62326693\n",
355 | " 0.70656875 0.47564428 0.57628063 0.36149511 1.28443402]\n",
356 | " [0.68002487 0.56872139 0.71613388 0.68846207 0.71301493 0.57422635\n",
357 | " 1.35048191 0.77289139 0.58578463 0.57403111 0.72263063 0.39298643\n",
358 | " 0.71657743 0.3017284 0.36846253 0.55643828 0.46561062 0.67219088\n",
359 | " 0.66536599 0.47665498 0.36173875 0.49229329 0.55685825 0.5388492\n",
360 | " 0.67278109 0.41338199 0.63107721 0.40505355 0.56518704]\n",
361 | " [0.9540385 1.06463549 1.06553088 1.32135157 0.92736842 0.92783679\n",
362 | " 0.77289139 2.1138036 0.79525 0.80262739 1.00421204 0.5480571\n",
363 | " 1.55356454 0.30302496 0.46719648 0.84032034 0.70486405 1.02298914\n",
364 | " 0.7633673 0.62992167 0.3658698 0.77258711 0.81982371 0.82657363\n",
365 | " 1.00074604 0.40619105 0.81891634 0.41893615 0.80896708]\n",
366 | " [0.72630968 0.65826615 0.7474291 0.79609702 0.72425245 0.59651661\n",
367 | " 0.58578463 0.79525 1.3897185 0.61904803 0.74277819 0.46273836\n",
368 | " 0.75294425 0.34308617 0.47212001 0.64962548 0.4866731 0.78442675\n",
369 | " 0.81991843 0.5270469 0.38869254 0.54309905 0.65908572 0.59748122\n",
370 | " 0.79335092 0.40774674 0.6886112 0.55004085 0.51060107]\n",
371 | " [0.70945859 0.66251941 0.77691868 0.88512353 0.81719807 0.67870537\n",
372 | " 0.57403111 0.80262739 0.61904803 1.63226569 0.84349145 0.46439182\n",
373 | " 0.76717143 0.37157516 0.39096364 0.65073111 0.57614062 0.87286369\n",
374 | " 0.59226376 0.51273082 0.42835501 0.5309683 0.56366909 0.72098388\n",
375 | " 0.75236424 0.4445684 0.53380786 0.42994184 0.62132567]\n",
376 | " [1.07333017 0.73789772 0.96595019 1.15246617 1.15660938 0.80956635\n",
377 | " 0.72263063 1.00421204 0.74277819 0.84349145 2.5195914 0.55940497\n",
378 | " 0.92434812 0.39589367 0.44534822 0.86494966 0.64920211 1.33841005\n",
379 | " 0.74573698 0.64343442 0.44494161 0.61596263 0.71733199 0.76577121\n",
380 | " 0.94849863 0.45110502 0.7420937 0.45376612 0.74855107]\n",
381 | " [0.48572094 0.47196466 0.56720978 0.53207219 0.58309596 0.50655966\n",
382 | " 0.39298643 0.5480571 0.46273836 0.46439182 0.55940497 1.00141421\n",
383 | " 0.54407322 0.37413977 0.42465458 0.59482171 0.64932007 0.58741227\n",
384 | " 0.47386734 0.59794565 0.42766657 0.4613535 0.55089357 0.47737998\n",
385 | " 0.54672332 0.42971094 0.57914349 0.40038253 0.5033388 ]\n",
386 | " [0.8251006 1.00101545 0.97737004 1.16862013 0.90853687 0.92523497\n",
387 | " 0.71657743 1.55356454 0.75294425 0.76717143 0.92434812 0.54407322\n",
388 | " 1.70226421 0.32373671 0.4832029 0.7922617 0.67528448 0.92505458\n",
389 | " 0.72935902 0.63717426 0.36924885 0.78505295 0.76115789 0.7564726\n",
390 | " 0.88179126 0.42799605 0.71716322 0.429858 0.79929981]\n",
391 | " [0.30601011 0.30743029 0.38099252 0.3575928 0.36218917 0.38139009\n",
392 | " 0.3017284 0.30302496 0.34308617 0.37157516 0.39589367 0.37413977\n",
393 | " 0.32373671 0.80620631 0.36013003 0.40569467 0.3838862 0.43059045\n",
394 | " 0.36992108 0.29980306 0.47161157 0.41169434 0.30970825 0.34156821\n",
395 | " 0.3566923 0.3967689 0.35538981 0.34610452 0.34374569]\n",
396 | " [0.45823283 0.35037656 0.47170257 0.45532119 0.47658137 0.43457364\n",
397 | " 0.36846253 0.46719648 0.47212001 0.39096364 0.44534822 0.42465458\n",
398 | " 0.4832029 0.36013003 1.08645862 0.42034437 0.40168401 0.57180894\n",
399 | " 0.45589715 0.34922409 0.36113169 0.42965168 0.43029041 0.36170601\n",
400 | " 0.49314053 0.35738508 0.43294896 0.37945512 0.38461827]\n",
401 | " [0.73218 0.65268493 0.87223471 1.06821468 0.80528362 0.6903186\n",
402 | " 0.55643828 0.84032034 0.64962548 0.65073111 0.86494966 0.59482171\n",
403 | " 0.7922617 0.40569467 0.42034437 1.23949227 0.58756374 0.82671503\n",
404 | " 0.64157778 0.55172153 0.41912491 0.62094738 0.62246677 0.74508991\n",
405 | " 0.74666898 0.40804632 0.62544734 0.42671961 0.64933174]\n",
406 | " [0.53433977 0.55568077 0.57833707 0.61626811 0.64732853 0.58157564\n",
407 | " 0.46561062 0.70486405 0.4866731 0.57614062 0.64920211 0.64932007\n",
408 | " 0.67528448 0.3838862 0.40168401 0.58756374 1.5043898 0.63257047\n",
409 | " 0.51638898 0.79082276 0.41487843 0.48436208 0.61451124 0.47522323\n",
410 | " 0.6132868 0.48144284 0.64004378 0.45948971 0.56611981]\n",
411 | " [1.22660419 0.7945144 0.92506778 1.10384461 1.14200595 0.80664559\n",
412 | " 0.67219088 1.02298914 0.78442675 0.87286369 1.33841005 0.58741227\n",
413 | " 0.92505458 0.43059045 0.57180894 0.82671503 0.63257047 2.12289303\n",
414 | " 0.84515648 0.65477087 0.4918338 0.62489805 0.81476537 0.76295069\n",
415 | " 1.17480747 0.45864529 0.83234273 0.4839239 0.66480043]\n",
416 | " [0.77524223 0.68112631 0.8218734 0.78896131 0.78507677 0.54177056\n",
417 | " 0.66536599 0.7633673 0.81991843 0.59226376 0.74573698 0.47386734\n",
418 | " 0.72935902 0.36992108 0.45589715 0.64157778 0.51638898 0.84515648\n",
419 | " 2.2110946 0.49981829 0.3854958 0.51341632 0.66458782 0.59963521\n",
420 | " 0.83936583 0.36567803 0.68944143 0.51048011 0.49289368]\n",
421 | " [0.5444771 0.54156063 0.53242063 0.60979315 0.62237188 0.50672642\n",
422 | " 0.47665498 0.62992167 0.5270469 0.51273082 0.64343442 0.59794565\n",
423 | " 0.63717426 0.29980306 0.34922409 0.55172153 0.79082276 0.65477087\n",
424 | " 0.49981829 1.22879713 0.36723356 0.4610153 0.6697631 0.50914121\n",
425 | " 0.60315567 0.38318442 0.64267657 0.42400373 0.50641961]\n",
426 | " [0.41917661 0.34473927 0.41390469 0.39121348 0.44128741 0.42583641\n",
427 | " 0.36173875 0.3658698 0.38869254 0.42835501 0.44494161 0.42766657\n",
428 | " 0.36924885 0.47161157 0.36113169 0.41912491 0.41487843 0.4918338\n",
429 | " 0.3854958 0.36723356 0.89602256 0.39177289 0.36013677 0.37091905\n",
430 | " 0.41507424 0.41455209 0.43448343 0.41645975 0.40484976]\n",
431 | " [0.50943202 0.53506318 0.62206326 0.67869145 0.58736908 0.5708276\n",
432 | " 0.49229329 0.77258711 0.54309905 0.5309683 0.61596263 0.4613535\n",
433 | " 0.78505295 0.41169434 0.42965168 0.62094738 0.48436208 0.62489805\n",
434 | " 0.51341632 0.4610153 0.39177289 1.05609328 0.57490249 0.5598936\n",
435 | " 0.5672914 0.42760787 0.55136283 0.38799285 0.51716774]\n",
436 | " [0.77433417 0.71830933 0.74452648 0.72009107 0.65635776 0.58621616\n",
437 | " 0.55685825 0.81982371 0.65908572 0.56366909 0.71733199 0.55089357\n",
438 | " 0.76115789 0.30970825 0.43029041 0.62246677 0.61451124 0.81476537\n",
439 | " 0.66458782 0.6697631 0.36013677 0.57490249 1.72193806 0.59735461\n",
440 | " 0.74636076 0.35659433 0.78012842 0.48215088 0.53046732]\n",
441 | " [0.69274799 0.67723734 0.93342475 0.98562753 0.73868178 0.62326693\n",
442 | " 0.5388492 0.82657363 0.59748122 0.72098388 0.76577121 0.47737998\n",
443 | " 0.7564726 0.34156821 0.36170601 0.74508991 0.47522323 0.76295069\n",
444 | " 0.59963521 0.50914121 0.37091905 0.5598936 0.59735461 1.30072769\n",
445 | " 0.73576514 0.37269622 0.62108866 0.44736971 0.57223138]\n",
446 | " [1.0086701 0.84700893 0.92483951 0.96349618 0.9396166 0.70656875\n",
447 | " 0.67278109 1.00074604 0.79335092 0.75236424 0.94849863 0.54672332\n",
448 | " 0.88179126 0.3566923 0.49314053 0.74666898 0.6132868 1.17480747\n",
449 | " 0.83936583 0.60315567 0.41507424 0.5672914 0.74636076 0.73576514\n",
450 | " 1.70685388 0.33726478 0.72017945 0.42402111 0.62643838]\n",
451 | " [0.29354661 0.34247718 0.42031728 0.400679 0.45371174 0.47564428\n",
452 | " 0.41338199 0.40619105 0.40774674 0.4445684 0.45110502 0.42971094\n",
453 | " 0.42799605 0.3967689 0.35738508 0.40804632 0.48144284 0.45864529\n",
454 | " 0.36567803 0.38318442 0.41455209 0.42760787 0.35659433 0.37269622\n",
455 | " 0.33726478 1.15714096 0.48910367 0.41384225 0.41153949]\n",
456 | " [0.69735896 0.66688086 0.67937008 0.68112899 0.70654156 0.57628063\n",
457 | " 0.63107721 0.81891634 0.6886112 0.53380786 0.7420937 0.57914349\n",
458 | " 0.71716322 0.35538981 0.43294896 0.62544734 0.64004378 0.83234273\n",
459 | " 0.68944143 0.64267657 0.43448343 0.55136283 0.78012842 0.62108866\n",
460 | " 0.72017945 0.48910367 2.55412679 0.62471282 0.50655322]\n",
461 | " [0.44587945 0.37953128 0.50859443 0.43156621 0.56006952 0.36149511\n",
462 | " 0.40505355 0.41893615 0.55004085 0.42994184 0.45376612 0.40038253\n",
463 | " 0.429858 0.34610452 0.37945512 0.42671961 0.45948971 0.4839239\n",
464 | " 0.51048011 0.42400373 0.41645975 0.38799285 0.48215088 0.44736971\n",
465 | " 0.42402111 0.41384225 0.62471282 1.50880951 0.36486707]\n",
466 | " [0.63272823 0.52158702 0.72979658 1.05224209 0.70953946 1.28443402\n",
467 | " 0.56518704 0.80896708 0.51060107 0.62132567 0.74855107 0.5033388\n",
468 | " 0.79929981 0.34374569 0.38461827 0.64933174 0.56611981 0.66480043\n",
469 | " 0.49289368 0.50641961 0.40484976 0.51716774 0.53046732 0.57223138\n",
470 | " 0.62643838 0.41153949 0.50655322 0.36486707 1.41401567]]\n"
471 | ]
472 | }
473 | ],
474 | "source": [
475 | "#compute mean returns and variance covariance matrix of returns\n",
476 | "meanReturns = np.mean(stockReturns, axis = 0)\n",
477 | "print('Mean returns of Dow Stocks:\\n', meanReturns)\n",
478 | "covReturns = np.cov(stockReturns, rowvar=False)\n",
479 | "print('Variance-covariance matrix of returns of Dow Stocks:\\n')\n",
480 | "print(covReturns)"
481 | ]
482 | },
483 | {
484 | "cell_type": "markdown",
485 | "metadata": {},
486 | "source": [
487 | "## 1.5 Beta "
488 | ]
489 | },
490 | {
491 | "cell_type": "markdown",
492 | "metadata": {},
493 | "source": [
494 | "It is a fact that stocks which deliver high returns are prone to high risk. **Beta** $(\\beta)$ or **Beta coefficient**, also referred to as **financial elasticity** helps to determine the asset risk with regard to that of the markets. Beta measures the volatility or systematic risk of an investment and is computed as, "
495 | ]
496 | },
497 | {
498 | "cell_type": "markdown",
499 | "metadata": {},
500 | "source": [
501 | "\n",
502 | "..........(1.2)
"
503 | ]
504 | },
505 | {
506 | "cell_type": "markdown",
507 | "metadata": {},
508 | "source": [
509 | "where $cov(r_i, r_P)$ is the covariance of the rate of return $r_i$ of the asset $i$ in a portfolio P and $r_P$, the rate of return of the portfolio P. $var(r_P)$ is the variance of the rate of return $r_P$ of the portfolio P. In practice, the portfolio return $r_P$ is replaced by the market index return that was explained in Sec. 1.3.\n",
510 | "\n",
511 | "The interpretation of $\\beta$ is as follows:\n",
512 | "$\\beta=0$ implies that the stock return is uncorrelated with the market movements,\n",
513 | "$\\beta <0$ implies that the stock return inversely follows the market movements, and\n",
514 | "$\\beta >0$ implies that the stock return follows the market movements. \n",
515 | "For the last case, if $\\beta <1$, then the stock is less volatile than the market, if $\\beta>1$, then the stock is more volatile than the market and if $\\beta =1$, then the stock is as volatile as the market. "
516 | ]
517 | },
518 | {
519 | "cell_type": "markdown",
520 | "metadata": {},
521 | "source": [
522 | "Let us suppose an investor decides to hold a **portfolio P** of the following 15 Dow stocks:\n",
523 | "'AAPL', 'AXP', 'BA', 'CAT', 'CSCO', 'DIS', 'GS', 'HD', 'IBM', 'JPM', 'KO', 'MCD', 'MRK', 'UNH', 'WBA'. The investor wishes to investigate the volatilities of the assets and therefore computes the individual asset betas over a 3-Year market data ( April 2016 -April 2019). The Python code to compute asset betas is shown below:"
524 | ]
525 | },
526 | {
527 | "cell_type": "code",
528 | "execution_count": 51,
529 | "metadata": {},
530 | "outputs": [
531 | {
532 | "name": "stdout",
533 | "output_type": "stream",
534 | "text": [
535 | "Portfolio stocks\n",
536 | " ['AAPL', 'AXP', 'BA', 'CAT', 'CSCO', 'DIS', 'GS', 'HD', 'IBM', 'JPM', 'KO', 'MCD', 'MRK', 'UNH', 'WBA']\n",
537 | "Asset Betas: \n",
538 | "\n",
539 | " 1.134\n",
540 | " 1.087\n",
541 | " 1.392\n",
542 | " 1.527\n",
543 | " 1.154\n",
544 | " 0.767\n",
545 | " 1.317\n",
546 | " 0.937\n",
547 | " 0.976\n",
548 | " 1.115\n",
549 | " 0.460\n",
550 | " 0.554\n",
551 | " 0.735\n",
552 | " 0.950\n",
553 | " 0.850\n"
554 | ]
555 | }
556 | ],
557 | "source": [
558 | "#compute betas of Dow stocks over a 3-year historical period,\n",
559 | "#DJIA Index- April 2016 to April 2019\n",
560 | "\n",
561 | "#dependencies\n",
562 | "import numpy as np\n",
563 | "import pandas as pd\n",
564 | "\n",
565 | "#input stock prices and market datasets\n",
566 | "stockFileName = 'DJIAkpf1Apr2016to20193YBeta.csv'\n",
567 | "marketFileName = 'DJIAMarketDataApr2016to20193YBeta.csv'\n",
568 | "stockRows = 756 #excluding header \n",
569 | "stockColumns = 15 #excluding date\n",
570 | "marketRows = 756\n",
571 | "marketColumns = 7\n",
572 | "\n",
573 | "#read stock prices dataset and market dataset \n",
574 | "dfStock = pd.read_csv(stockFileName, nrows= stockRows)\n",
575 | "dfMarket = pd.read_csv(marketFileName, nrows = marketRows)\n",
576 | "\n",
577 | "#extract asset labels of stocks in the portfolio\n",
578 | "assetLabels = dfStock.columns[1:stockColumns+1].tolist()\n",
579 | "print('Portfolio stocks\\n', assetLabels)\n",
580 | "\n",
581 | "#extract asset prices data and market data\n",
582 | "stockData = dfStock.iloc[0:, 1:]\n",
583 | "marketData = dfMarket.iloc[0:, [4]] #closing price \n",
584 | "\n",
585 | "#compute asset returns\n",
586 | "arrayStockData = np.asarray(stockData)\n",
587 | "[sRows, sCols]=arrayStockData.shape\n",
588 | "stockReturns = StockReturnsComputing(arrayStockData, sRows, sCols)\n",
589 | "\n",
590 | "#compute market returns\n",
591 | "arrayMarketData = np.asarray(marketData)\n",
592 | "[mRows, mCols]=arrayMarketData.shape\n",
593 | "marketReturns = StockReturnsComputing(arrayMarketData, mRows, mCols)\n",
594 | "\n",
595 | "#compute betas of assets in the portfolio\n",
596 | "beta= []\n",
597 | "Var = np.var(marketReturns, ddof =1)\n",
598 | "for i in range(stockColumns):\n",
599 | " CovarMat = np.cov(marketReturns[:,0], stockReturns[:, i ])\n",
600 | " Covar = CovarMat[1,0]\n",
601 | " beta.append(Covar/Var)\n",
602 | " \n",
603 | " \n",
604 | "#output betas of assets in the portfolio\n",
605 | "print('Asset Betas: \\n')\n",
606 | "for data in beta:\n",
607 | " print('{:9.3f}'.format(data))\n"
608 | ]
609 | },
610 | {
611 | "cell_type": "markdown",
612 | "metadata": {},
613 | "source": [
614 | "A careful observation of the betas of the Dow stocks reveal that AAPL, AXP, BA, CAT, CSCO, GS and JPM, whose betas are greater than 1, are notionally more volatile than the market, while the rest of the stocks in the portfolio whose betas are less than 1 ( but > 0), are notionally less volatile than the market. A stock with beta equal to 1, moves with the market. Thus, IBM and UNH could be viewed as moving with the market. "
615 | ]
616 | },
617 | {
618 | "cell_type": "markdown",
619 | "metadata": {},
620 | "source": [
621 | "## 1.6 Portfolio weights and Portfolio Beta"
622 | ]
623 | },
624 | {
625 | "cell_type": "markdown",
626 | "metadata": {},
627 | "source": [
628 | "The amount of capital invested in assets comprising a portfolio, by the investor is termed **weights**. Portfolio weights therefore indicate the proportion of investments made over the assets comprising the portfolio. Though portfolio weights are practically expressed as a percentage, it is convenient to express them as lying in the interval [0-1], where 0 weights indicate nil investment in the asset and 1 indicates full investment of capital in an asset. An interval (0,1], would mean that every asset in the portfolio must have some portion of the capital invested in it. A portfolio whose weights sum up to 1, is called a *fully invested portfolio* and a portfolio whose weights are equally distributed is called as *equal weighted portfolio*. \n",
629 | "\n",
630 | "With regard to portfolios with equity stocks, **portfolio beta** is the weighted sum of the asset betas,where the weights are represented by the portfolio weights. Portfolio beta is given by, $\\sum{\\beta_i.W_i}$, where $\\beta_i$ are the asset betas and $W_i$ are the portfolio weights. "
631 | ]
632 | },
633 | {
634 | "cell_type": "markdown",
635 | "metadata": {},
636 | "source": [
637 | "## 1.7 Portfolio Return and Portfolio Risk"
638 | ]
639 | },
640 | {
641 | "cell_type": "markdown",
642 | "metadata": {},
643 | "source": [
644 | "Harry Markowtiz [MAR 52] proposed a framework known as **Mean-Variance Analysis** which won him the 1990 Nobel Prize in Economics. The framework laid the foundation for what is known as **Modern Portfolio Theory**. It viewed **portfolio return** as the capital gain that can be expected by holding a portfolio and **portfolio risk** as the extent of capital losses that it can suffer possibly due to adverse market movements. Portfolio return and risk were modelled using the *mean* and *variance* of the portfolio's fluctuations respectively. \n",
645 | "\n",
646 | "Portfolio return is defined as follows:\n",
647 | "Let P be a portfolio comprising assets $A_1, A_2,...A_N$ with weights $W_1, W_2,...W_N$ and $r_1, r_2, ...r_N$ as the asset returns. The portfolio return *r* determined by a weighted summation of its individual asset returns is given by, "
648 | ]
649 | },
650 | {
651 | "cell_type": "markdown",
652 | "metadata": {},
653 | "source": [
654 | "\n",
655 | "..........(1.3)
\n"
656 | ]
657 | },
658 | {
659 | "cell_type": "markdown",
660 | "metadata": {},
661 | "source": [
662 | "The **annualized portfolio return** assuming that the number of trading days in a year were $T_y$ ( for example, 261 out of 365 days in a year), is given by,"
663 | ]
664 | },
665 | {
666 | "cell_type": "markdown",
667 | "metadata": {},
668 | "source": [
669 | "\n",
670 | "..........(1.4)
\n"
671 | ]
672 | },
673 | {
674 | "cell_type": "markdown",
675 | "metadata": {},
676 | "source": [
677 | "Portfolio risk is the standard deviation of its returns and is given by,"
678 | ]
679 | },
680 | {
681 | "cell_type": "markdown",
682 | "metadata": {},
683 | "source": [
684 | "\n",
685 | "..........(1.5)
\n"
686 | ]
687 | },
688 | {
689 | "cell_type": "markdown",
690 | "metadata": {},
691 | "source": [
692 | "where $\\sigma_{ij}$ is the covariance of returns between assets i and j of the portfolio, also referred to as the variance-covariance matrix of returns. Standard deviation is a prominent measure of dispersion of data borrowed from Statistics and describes the deviation or spread of the data from its mean. In the case of portfolios, standard deviation of its returns, which is its risk, helps to measure the consistency of the returns. \n",
693 | "The **annualized portfolio risk** in percentage, $\\sigma_{Ann}$ is given by,"
694 | ]
695 | },
696 | {
697 | "cell_type": "markdown",
698 | "metadata": {},
699 | "source": [
700 | "\n",
701 | "..........(1.6)
\n"
702 | ]
703 | },
704 | {
705 | "cell_type": "markdown",
706 | "metadata": {},
707 | "source": [
708 | "where $T_y$ is the number of trading days in a year."
709 | ]
710 | },
711 | {
712 | "cell_type": "markdown",
713 | "metadata": {},
714 | "source": [
715 | "Risk-Return Tradeoff
\n",
716 | "\n",
717 | "It is an established trading principle that investments giving high returns are always associated with a probability of high risks and those that are less risky are associated with a probability of low returns. A typical example is when an investor facing retirement from service, chooses to invest her hard-earned retirement benefits in bank deposits or government bonds which are risk free (and therefore yield low returns), rather than investing them in stock markets which despite their potential to yield massive returns are bound by risks. Again, time plays a significant role while managing the risk - reward interplay of investments. For example, a long term investment in high yielding stocks in the stock market or real estate, can help appreciation of the investor's capital, while offering the potential to recover from risks. The risk-return tradeoff therefore, rests on many factors such as investor's risk appetite, holding period of securities and the potential to make up for losses. "
718 | ]
719 | },
720 | {
721 | "cell_type": "markdown",
722 | "metadata": {},
723 | "source": [
724 | "1.8 Case Study
"
725 | ]
726 | },
727 | {
728 | "cell_type": "markdown",
729 | "metadata": {},
730 | "source": [
731 | "Let us consider the portfolio P of Dow stocks described in Sec. 1.5. Let us suppose that a moderately risk-averse investor having realized the volatility of stocks after computing the asset betas, decides to invest 40% of the capital in stocks with high volatility ($\\beta >1$) and 60% of the capital on other stocks which are less volatile or will move with the market ($\\beta \\le 1$). The portfolio weights reflecting the same are shown in the table. The red banded tickers indicate high volatility stocks, the green banded tickers indicate low volatility stocks and blue banded stocks indicate those that will move with the market.\n"
732 | ]
733 | },
734 | {
735 | "cell_type": "markdown",
736 | "metadata": {},
737 | "source": [
738 | "Thus, if the investor owns a capital of USD 10,000, the investor decides to invest USD 6000 on less risky stocks and USD 4000 on risky stocks. The apportioning of the capital between individual stocks are determined by the weights, which let us suppose the investor chose to do it according to her own whims and fancies! Thus,stocks AAPL, AXP, BA, CAT, CSCO, GS and JPM, which are highly volatile are allotted 9%, 7%, 3%, 2%, 7%, 4% and 8% of the capital respectively, that adds up to USD 4000. Stocks DIS, HD, IBM, KO, MCD, MRK, UNH and WBA are allotted 6%,7%, 11%, 9%, 7%, 5%, 11% and 4% respectively, that adds up to USD 6000. "
739 | ]
740 | },
741 | {
742 | "cell_type": "markdown",
743 | "metadata": {},
744 | "source": [
745 | "\n",
746 | "Fig. 1.3 Asset betas and portfolio weights of 15 Dow stocks held by an investor for Portfolio P
\n",
747 | " "
748 | ]
749 | },
750 | {
751 | "cell_type": "markdown",
752 | "metadata": {},
753 | "source": [
754 | " \n",
755 | "The Python code fragment to compute the portfolio return and risk is given below. The portfolio weights and asset betas have been provided as direct inputs in the code to aid brevity of presentation."
756 | ]
757 | },
758 | {
759 | "cell_type": "code",
760 | "execution_count": 52,
761 | "metadata": {},
762 | "outputs": [
763 | {
764 | "name": "stdout",
765 | "output_type": "stream",
766 | "text": [
767 | "\n",
768 | " Annualized Portfolio Risk: 12.54 %\n",
769 | "\n",
770 | " Annualized Expected Portfolio Return: 14.94 %\n",
771 | "\n",
772 | " Portfolio Beta:0.95\n"
773 | ]
774 | }
775 | ],
776 | "source": [
777 | "#portfolio risk, expected return and portfolio beta computation\n",
778 | "\n",
779 | "#input weights and asset betas for portfolio P as described in Sec. 1.5\n",
780 | "weights = np.array([0.09, 0.07, 0.03, 0.02, 0.07, 0.06, 0.04, 0.07, 0.11, \\\n",
781 | " 0.08, 0.09, 0.07, 0.05, 0.11, 0.04])\n",
782 | "assetBeta = np.array([1.13, 1.09, 1.39, 1.53, 1.15, 0.77, 1.32, 0.94, 0.98,\\\n",
783 | " 1.12, 0.46, 0.55, 0.74, 0.95, 0.85])\n",
784 | "\n",
785 | "#compute mean and covariance of asset returns of portfolio P available in stockReturns\n",
786 | "meanReturns = np.mean(stockReturns, axis = 0)\n",
787 | "covReturns = np.cov(stockReturns, rowvar=False)\n",
788 | "\n",
789 | "#compute portfolio risk\n",
790 | "portfolioRisk = np.matmul((np.matmul(weights,covReturns)), np.transpose(weights))\n",
791 | "\n",
792 | "#compute annualized portfolio risk for trading days = 251\n",
793 | "annualizedRisk = np.sqrt(portfolioRisk*251) \n",
794 | "\n",
795 | "#compute expected portfolio return\n",
796 | "portfolioReturn = np.matmul(np.array(meanReturns),weights.T)\n",
797 | "\n",
798 | "#compute annualized expected portfolio return\n",
799 | "annualizedReturn = 251*np.array(portfolioReturn) \n",
800 | "\n",
801 | "#compute portfolio beta\n",
802 | "portfolioBeta = np.matmul(assetBeta,weights.T)\n",
803 | "\n",
804 | "#display results\n",
805 | "print(\"\\n Annualized Portfolio Risk: %4.2f\" % annualizedRisk,\"%\")\n",
806 | "print(\"\\n Annualized Expected Portfolio Return: %4.2f\" % annualizedReturn,\"%\")\n",
807 | "print(\"\\n Portfolio Beta:%4.2f\" % portfolioBeta)\n"
808 | ]
809 | },
810 | {
811 | "cell_type": "markdown",
812 | "metadata": {},
813 | "source": [
814 | " Investment analysis of Portfolio P
\n",
815 | "\n",
816 | "The risk-averse investor was playing moderately safe by investing only 40% of the capital in stocks with high volatility and 60% in stocks with less volatility. The portfolio yields an annual expected return of 14.94%. If one were to compare the returns with those of 10-year Treasury Bond (T-Bonds) issued by the US Government which are fixed interest debt securities that are risk-free (investors receive fixed interest payments until maturity date), the average annual return of T-Bonds for the period 2014 - 2018 was 3.1%, with a high of 10.75% during 2014. Again, comparing the returns of the portfolio with that of the Dow Jones Industrial Average market index, the average annual rate of return has been 5.42%. Thus, in this specific case, portfolio P was able to beat the market! \n",
817 | "\n",
818 | " \n",
819 | "However, the annualized risk of 12.54% needs to be seriously looked at. As explained earlier, the risk is a measure of the consistency of the return. If the investor's risk appetite can accommodate this, then all is well! \n",
820 | "Happy investing!"
821 | ]
822 | },
823 | {
824 | "cell_type": "markdown",
825 | "metadata": {},
826 | "source": [
827 | "Companion Reading
\n",
828 | "\n",
829 | "This blog is an abridged adaptation of concepts discussed in Chapter 1 of [PAI 18] to Dow Jones dataset (DJIA index: April, 2014- April, 2019) and implemented in Python. Readers (read \"worker bees\"), seeking more information may refer to the corresponding chapter in the book.\n"
830 | ]
831 | },
832 | {
833 | "cell_type": "markdown",
834 | "metadata": {},
835 | "source": [
836 | "References
\n",
837 | "\n",
838 | "[MAR 52] Markowitz H., Portfolio Selection, *The Journal of Finance*, vol. 7, no. 1, \n",
839 | " pp. 77-91, Mar., 1952.\n",
840 | "\n",
841 | "[PAI 18] Vijayalakshmi Pai G. A., Metaheuristics for Portfolio Optimization- *An \n",
842 | " Introduction using MATLAB*, Wiley-ISTE, 2018. https://www.mathworks.com/academia/books/metaheuristics-for-portfolio-optimization-pai.html"
843 | ]
844 | },
845 | {
846 | "cell_type": "markdown",
847 | "metadata": {},
848 | "source": [
849 | "##### Next .....Lesson2: Some Glimpses of Financial Data Wrangling \n"
850 | ]
851 | },
852 | {
853 | "cell_type": "markdown",
854 | "metadata": {},
855 | "source": [
856 | ""
857 | ]
858 | },
859 | {
860 | "cell_type": "markdown",
861 | "metadata": {},
862 | "source": [
863 | ""
869 | ]
870 | },
871 | {
872 | "cell_type": "markdown",
873 | "metadata": {},
874 | "source": [
875 | ""
881 | ]
882 | },
883 | {
884 | "cell_type": "code",
885 | "execution_count": null,
886 | "metadata": {},
887 | "outputs": [],
888 | "source": []
889 | }
890 | ],
891 | "metadata": {
892 | "kernelspec": {
893 | "display_name": "Python 3",
894 | "language": "python",
895 | "name": "python3"
896 | },
897 | "language_info": {
898 | "codemirror_mode": {
899 | "name": "ipython",
900 | "version": 3
901 | },
902 | "file_extension": ".py",
903 | "mimetype": "text/x-python",
904 | "name": "python",
905 | "nbconvert_exporter": "python",
906 | "pygments_lexer": "ipython3",
907 | "version": "3.7.3"
908 | }
909 | },
910 | "nbformat": 4,
911 | "nbformat_minor": 2
912 | }
913 |
--------------------------------------------------------------------------------
/Lesson2_FinDataWrangling/Lesson2ExitTailImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaiViji/PythonFinance-PortfolioOptimization/2bb0ae9f32f44ff66302ea33f53c02e746583a05/Lesson2_FinDataWrangling/Lesson2ExitTailImage.png
--------------------------------------------------------------------------------
/Lesson2_FinDataWrangling/Lesson2Fig2_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaiViji/PythonFinance-PortfolioOptimization/2bb0ae9f32f44ff66302ea33f53c02e746583a05/Lesson2_FinDataWrangling/Lesson2Fig2_1.png
--------------------------------------------------------------------------------
/Lesson2_FinDataWrangling/Lesson2Fig2_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaiViji/PythonFinance-PortfolioOptimization/2bb0ae9f32f44ff66302ea33f53c02e746583a05/Lesson2_FinDataWrangling/Lesson2Fig2_2.png
--------------------------------------------------------------------------------
/Lesson2_FinDataWrangling/Lesson2FinDataWranglingSampledata.csv:
--------------------------------------------------------------------------------
1 | ,RELIANCE INDUSTRIES LIMITED,INFOSYS TECHNOLOGIES LTD,ITC LTD,BHARTI AIRTEL LIMITED,HOUSING DEVELOPMENT FINANCE,LARSEN & TOUBRO LIMITED,ICICI BANK LTD,HDFC BANK LIMITED,OIL & NATURAL GAS CORP LTD,STATE BANK OF INDIA,HINDUSTAN UNILEVER LIMITED,BHARAT HEAVY ELECTRICALS,NTPC LIMITED,TATA CONSULTANCY SVS LTD,GAIL INDIA LTD,CIPLA LTD,GRASIM INDUSTRIES LTD,TATA POWER CO LTD
17/02/02,,,,45,,,,,,,,,,,,,,
18/02/02,245.23,466.28,49.84,44.15,338.6,81.53,127.8,248.45,152.57,229.09,241.35,94.33,,,54.97,84.12,315.51,133.35
19/02/02,246.77,462.74,50.05,41.2,337.35,79.56,125.25,234.85,150.27,226.3,241,86.93,,,53.63,84.32,311.01,130.3
20/02/02,244.11,456.34,48.49,41.4,334.82,76.94,124.3,236,155.37,226.82,239.95,85.8,,,52.33,85.21,301.66,126.5
21/02/02,241.34,457.45,49.16,42.45,330.13,76.57,127.5,236.3,152.57,230.41,240.65,85.3,,,52.03,85.22,299.76,126.8
22/02/02,242.15,464.64,49.97,43.45,335,76.25,140.25,247,156.73,227.81,240.5,88.23,,,51.93,84.92,299.06,129.9
23/02/02,,,,,,,,,,,,,,,,,,
24/02/02,,,,,,,,,,,,,,,,,,
25/02/02,242.11,468.44,48.42,42.65,336.48,76.27,140.25,243.8,155.37,228.1,239.1,86.63,,,49.2,85.3,298.71,130.2
26/02/02,247.5,480.57,48.33,43.55,340.82,77.47,135.45,237.7,161.27,245.55,241.55,91.45,,,51.4,83.87,300.96,131.45
27/02/02,249.8,479.23,48.7,44.3,333.52,78.12,131.55,235.6,157.13,238.81,246.5,93.08,,,49.37,84.1,307.61,130.8
28/02/02,236.5,441.28,49.77,40.9,340.9,73.34,125.55,235.2,149.13,214.93,249.8,84.08,,,47.27,80.44,289.21,119.3
,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,
--------------------------------------------------------------------------------
/Lesson2_FinDataWrangling/Lesson2GoalHeaderImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaiViji/PythonFinance-PortfolioOptimization/2bb0ae9f32f44ff66302ea33f53c02e746583a05/Lesson2_FinDataWrangling/Lesson2GoalHeaderImage.png
--------------------------------------------------------------------------------
/Lesson2_FinDataWrangling/Lesson2MissingValBSE200.csv:
--------------------------------------------------------------------------------
1 | ,JINDAL SAW LTD,BAJAJ HINDUSTHAN LIMITED,LAKSHMI MACHINE WORKS LTD,GUJARAT MINERAL DEV CORP LTD,MOSER BAER INDIA LTD
16/08/01,60.85,5.1,69,4.8,86.08
17/08/01,60.7,,70,4.75,86.55
20/08/01,59.5,,,4.68,86.87
21/08/01,58.8,5.06,69.5,4.73,86.67
23/08/01,57.25,4.92,66.49,4.83,86.65
24/08/01,57.05,5.25,69.9,5.07,84.27
27/08/01,62.35,,70,5.01,82.45
28/08/01,66.55,4.86,,4.95,82.92
29/08/01,68.5,,70.5,4.96,83.62
30/08/01,69.65,,,4.95,84.22
31/08/01,65.75,4.86,70,4.94,83.2
--------------------------------------------------------------------------------
/Lesson2_FinDataWrangling/Lesson2_MainContent.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "### Lesson 2\n",
8 | " "
9 | ]
10 | },
11 | {
12 | "cell_type": "markdown",
13 | "metadata": {},
14 | "source": [
15 | "# Some Glimpses of Financial Data Wrangling "
16 | ]
17 | },
18 | {
19 | "cell_type": "markdown",
20 | "metadata": {},
21 | "source": [
22 | "\n",
23 | ""
24 | ]
25 | },
26 | {
27 | "cell_type": "markdown",
28 | "metadata": {},
29 | "source": [
30 | "## 2.1 Introduction"
31 | ]
32 | },
33 | {
34 | "cell_type": "markdown",
35 | "metadata": {},
36 | "source": [
37 | "The \"raw\" financial datasets based on which crucial investment decisions are taken, cannot be used on an as-is-where-is basis, despite the fact that these are dynamically generated on-line as the trading proceeds at the stock exchanges concerned. The financial datasets need to be cleaned and transformed to enable smooth functioning of the models that work over these datasets."
38 | ]
39 | },
40 | {
41 | "cell_type": "markdown",
42 | "metadata": {},
43 | "source": [
44 | "**Data wrangling** or **Data munging** or the more familiar **Data pre-processing**, deals with a slew of techniques that serve to clean the data, transform the data and render it fit for use by various models that will work over it, to undertake operations or analysis or research. "
45 | ]
46 | },
47 | {
48 | "cell_type": "markdown",
49 | "metadata": {},
50 | "source": [
51 | "With regard to \"raw\" financial datasets, the following problems may be encountered: \n",
52 | "\n",
53 | "(1) blank tuples (rows) in the dataset, and \n",
54 | "(2) missing values of certain fields in the dataset \n",
55 | "\n",
56 | "Fig. 2.1 illustrates a snapshot of a financial dataset extracted from S&P BSE200 (Bombay Stock Exchange, India) for the period Feb 17, 2002 to Feb 28, 2002, that has these aformentioned problems. The empty rows, columns and missing values, can be seen in the \"raw\" dataset."
57 | ]
58 | },
59 | {
60 | "attachments": {},
61 | "cell_type": "markdown",
62 | "metadata": {},
63 | "source": [
64 | "\n",
65 | "#### Fig. 2.1 Snapshot of a finance dataset that calls for data wrangling\n"
66 | ]
67 | },
68 | {
69 | "cell_type": "markdown",
70 | "metadata": {},
71 | "source": [
72 | "## 2.2 Eliminating empty rows"
73 | ]
74 | },
75 | {
76 | "cell_type": "markdown",
77 | "metadata": {},
78 | "source": [
79 | "Considering the size of the finance datasets, which needless to say can be very huge, the removal of empty rows from the dataset can be accomplished by coding a function for the same. The Python function **EmptyRowsElimination**, reads the stock prices from the dataset to eliminate the empty rows in the dataset using **dropna** method of Pandas. "
80 | ]
81 | },
82 | {
83 | "cell_type": "code",
84 | "execution_count": 10,
85 | "metadata": {},
86 | "outputs": [],
87 | "source": [
88 | "#function to eliminate empty rows in a dataset\n",
89 | "def EmptyRowsElimination(dfAssetPrices):\n",
90 | "\n",
91 | " # read dataset and extract its dimensions\n",
92 | " [Rows, Columns] = dfAssetPrices.shape\n",
93 | " dFrame = dfAssetPrices.iloc[0:Rows, 0:Columns]\n",
94 | " \n",
95 | " # call dropna method from Pandas \n",
96 | " dFClean = dFrame.dropna(axis =0, how ='all')\n",
97 | " return dFClean"
98 | ]
99 | },
100 | {
101 | "cell_type": "markdown",
102 | "metadata": {},
103 | "source": [
104 | "The Python code invoking the function **EmptyRowsElimination** over the example dataset shown in Fig. 2.1 is given below. The output shows that the two empty rows corresponding to dates, 23-02-2002 and 24-02-2002 have been removed. "
105 | ]
106 | },
107 | {
108 | "cell_type": "code",
109 | "execution_count": 11,
110 | "metadata": {},
111 | "outputs": [
112 | {
113 | "name": "stdout",
114 | "output_type": "stream",
115 | "text": [
116 | "['RELIANCE INDUSTRIES LIMITED', 'INFOSYS TECHNOLOGIES LTD', 'ITC LTD', 'BHARTI AIRTEL LIMITED', 'HOUSING DEVELOPMENT FINANCE', 'LARSEN & TOUBRO LIMITED', 'ICICI BANK LTD', 'HDFC BANK LIMITED', 'OIL & NATURAL GAS CORP LTD', 'STATE BANK OF INDIA', 'HINDUSTAN UNILEVER LIMITED', 'BHARAT HEAVY ELECTRICALS', 'NTPC LIMITED', 'TATA CONSULTANCY SVS LTD', 'GAIL INDIA LTD', 'CIPLA LTD', 'GRASIM INDUSTRIES LTD', 'TATA POWER CO LTD']\n",
117 | "\n",
118 | "Data cleaning completed!\n",
119 | "Dimensions of the cleaned dataset (10, 18)\n",
120 | "Cleaned dataset: \n",
121 | " RELIANCE INDUSTRIES LIMITED INFOSYS TECHNOLOGIES LTD ITC LTD \\\n",
122 | "0 NaN NaN NaN \n",
123 | "1 245.23 466.28 49.84 \n",
124 | "2 246.77 462.74 50.05 \n",
125 | "3 244.11 456.34 48.49 \n",
126 | "4 241.34 457.45 49.16 \n",
127 | "5 242.15 464.64 49.97 \n",
128 | "8 242.11 468.44 48.42 \n",
129 | "9 247.50 480.57 48.33 \n",
130 | "10 249.80 479.23 48.70 \n",
131 | "11 236.50 441.28 49.77 \n",
132 | "\n",
133 | " BHARTI AIRTEL LIMITED HOUSING DEVELOPMENT FINANCE \\\n",
134 | "0 45.00 NaN \n",
135 | "1 44.15 338.60 \n",
136 | "2 41.20 337.35 \n",
137 | "3 41.40 334.82 \n",
138 | "4 42.45 330.13 \n",
139 | "5 43.45 335.00 \n",
140 | "8 42.65 336.48 \n",
141 | "9 43.55 340.82 \n",
142 | "10 44.30 333.52 \n",
143 | "11 40.90 340.90 \n",
144 | "\n",
145 | " LARSEN & TOUBRO LIMITED ICICI BANK LTD HDFC BANK LIMITED \\\n",
146 | "0 NaN NaN NaN \n",
147 | "1 81.53 127.80 248.45 \n",
148 | "2 79.56 125.25 234.85 \n",
149 | "3 76.94 124.30 236.00 \n",
150 | "4 76.57 127.50 236.30 \n",
151 | "5 76.25 140.25 247.00 \n",
152 | "8 76.27 140.25 243.80 \n",
153 | "9 77.47 135.45 237.70 \n",
154 | "10 78.12 131.55 235.60 \n",
155 | "11 73.34 125.55 235.20 \n",
156 | "\n",
157 | " OIL & NATURAL GAS CORP LTD STATE BANK OF INDIA \\\n",
158 | "0 NaN NaN \n",
159 | "1 152.57 229.09 \n",
160 | "2 150.27 226.30 \n",
161 | "3 155.37 226.82 \n",
162 | "4 152.57 230.41 \n",
163 | "5 156.73 227.81 \n",
164 | "8 155.37 228.10 \n",
165 | "9 161.27 245.55 \n",
166 | "10 157.13 238.81 \n",
167 | "11 149.13 214.93 \n",
168 | "\n",
169 | " HINDUSTAN UNILEVER LIMITED BHARAT HEAVY ELECTRICALS NTPC LIMITED \\\n",
170 | "0 NaN NaN NaN \n",
171 | "1 241.35 94.33 NaN \n",
172 | "2 241.00 86.93 NaN \n",
173 | "3 239.95 85.80 NaN \n",
174 | "4 240.65 85.30 NaN \n",
175 | "5 240.50 88.23 NaN \n",
176 | "8 239.10 86.63 NaN \n",
177 | "9 241.55 91.45 NaN \n",
178 | "10 246.50 93.08 NaN \n",
179 | "11 249.80 84.08 NaN \n",
180 | "\n",
181 | " TATA CONSULTANCY SVS LTD GAIL INDIA LTD CIPLA LTD \\\n",
182 | "0 NaN NaN NaN \n",
183 | "1 NaN 54.97 84.12 \n",
184 | "2 NaN 53.63 84.32 \n",
185 | "3 NaN 52.33 85.21 \n",
186 | "4 NaN 52.03 85.22 \n",
187 | "5 NaN 51.93 84.92 \n",
188 | "8 NaN 49.20 85.30 \n",
189 | "9 NaN 51.40 83.87 \n",
190 | "10 NaN 49.37 84.10 \n",
191 | "11 NaN 47.27 80.44 \n",
192 | "\n",
193 | " GRASIM INDUSTRIES LTD TATA POWER CO LTD \n",
194 | "0 NaN NaN \n",
195 | "1 315.51 133.35 \n",
196 | "2 311.01 130.30 \n",
197 | "3 301.66 126.50 \n",
198 | "4 299.76 126.80 \n",
199 | "5 299.06 129.90 \n",
200 | "8 298.71 130.20 \n",
201 | "9 300.96 131.45 \n",
202 | "10 307.61 130.80 \n",
203 | "11 289.21 119.30 \n"
204 | ]
205 | }
206 | ],
207 | "source": [
208 | "#empty rows elimination from stock prices dataset\n",
209 | "\n",
210 | "#dependencies\n",
211 | "import numpy as np\n",
212 | "import pandas as pd\n",
213 | "\n",
214 | "#input dataset and dimensions of the dataset\n",
215 | "StockFileName = 'Lesson2FinDataWranglingSampledata.csv'\n",
216 | "Rows = 12 #excluding headers\n",
217 | "Columns = 18 #excluding date\n",
218 | "\n",
219 | "#read stock prices \n",
220 | "df = pd.read_csv(StockFileName, nrows= Rows)\n",
221 | "\n",
222 | "#extract asset Names\n",
223 | "assetNames = df.columns[1:Columns+1].tolist()\n",
224 | "print(assetNames)\n",
225 | "\n",
226 | "#clean the stock dataset of empty rows\n",
227 | "StockData = df.iloc[0:, 1:]\n",
228 | "dfClean = EmptyRowsElimination(StockData)\n",
229 | "print('\\nData cleaning completed!')\n",
230 | "[rows, cols]=dfClean.shape\n",
231 | "print('Dimensions of the cleaned dataset', dfClean.shape)\n",
232 | "print('Cleaned dataset: \\n', dfClean)"
233 | ]
234 | },
235 | {
236 | "cell_type": "markdown",
237 | "metadata": {},
238 | "source": [
239 | "It can be clearly seen that rows 6 and 7 which held empty rows have been eliminated. However, the two empty columns corresponding to stocks NTPC Ltd and Tata Consultancy Services Ltd., continue to persist. To eliminate columns that are wholly empty one merely has to use **dropna** method with axis =1. \n",
240 | " \n",
241 | "However, this was not done for the given S&P BSE200 data set only to draw attention to the fact that empty columns in a stock dataset during a given period, may be indicative of the fact that the stocks were not listed on the exchange during the period concerned. Therefore, models which work on such datasets may either choose to eliminate such columns (ignoring short series data as it is said), if the time period is important for the study, or may choose to work over the dataset pulled for an appropriate time period, when the stocks were listed on the exchange and traded, thereby leaving no columns empty, for reasons otherwise. "
242 | ]
243 | },
244 | {
245 | "cell_type": "markdown",
246 | "metadata": {},
247 | "source": [
248 | "Summarizing, a finance dataset has to be essentially cleaned of all empty rows and columns, to enable successful working of models over it. "
249 | ]
250 | },
251 | {
252 | "cell_type": "markdown",
253 | "metadata": {},
254 | "source": [
255 | "## 2.3 Filling Missing Values"
256 | ]
257 | },
258 | {
259 | "cell_type": "markdown",
260 | "metadata": {},
261 | "source": [
262 | "Many a time, datasets are seen interspersed with quite a few empty fields which can only be attributed to improper handling of data downstream or errors that propagated during data handling at source. Termed **missing values** or **missing data** in data analytics, this is a serious problem that cannot be overlooked and needs to be redressed. Fortunately, a variety of methods are available to tackle missing data in a dataset. Different models working over these datasets adopt different techniques to tackle the missing values in the dataset, based on their justifications and requirements of analyses. \n"
263 | ]
264 | },
265 | {
266 | "cell_type": "markdown",
267 | "metadata": {},
268 | "source": [
269 | "This work, considering the fact that the dataset describes the daily prices of stocks traded on a particular day, the missing values are replaced by the previous day's trading price which is logical and therefore acceptable to the models that will work on such datasets. Needless to say, filling missing values using this method, insist on the following mandatory requirements: \n",
270 | "\n",
271 | "(1) The dataset should have been cleared of all empty rows and empty columns (as \n",
272 | " discussed in Sec. 2.2), and\n",
273 | "(2) The opening row of the dataset cannot have missing values. \n",
274 | "\n",
275 | "\n",
276 | "A snapshot of an S&P BSE200 dataset (Bombay Stock Exchange, India) during August 16, 2001 to August 31, 2001 is shown in Fig.2.2. Observe that the dataset is interspersed with quite a few missing values. The data set however, has been cleaned to remove empty rows /columns for the period concerned and the important requirement of the opening row not having any missing values has been ensured. "
277 | ]
278 | },
279 | {
280 | "cell_type": "markdown",
281 | "metadata": {},
282 | "source": [
283 | " \n",
284 | "#### Fig. 2.2 Snapshot of a finance dataset that calls for filling missing data values "
285 | ]
286 | },
287 | {
288 | "cell_type": "markdown",
289 | "metadata": {},
290 | "source": [
291 | "The function **FillMissingValues** undertakes to fill the missing stock prices with the previous day's prices for a dataset satisfying the mandatory requirements."
292 | ]
293 | },
294 | {
295 | "cell_type": "code",
296 | "execution_count": 12,
297 | "metadata": {},
298 | "outputs": [],
299 | "source": [
300 | "#function to fill missing values of daily stock prices\n",
301 | "#Mandatory requirements: (1) The dataset should have been cleaned of all empty rows \n",
302 | "#before missing values are filled, and \n",
303 | "#(2) the opening row of the dataset should not have any empty fields\n",
304 | "\n",
305 | "def FillMissingValues(StockPrices):\n",
306 | " \n",
307 | " import numpy as np\n",
308 | " print('Fill missing values...')\n",
309 | " \n",
310 | " #identify positions of the missing values in StockPrices\n",
311 | " [rows, cols] = np.where(np.asarray(np.isnan(StockPrices)))\n",
312 | " \n",
313 | " #replace missing value with the previous day's price\n",
314 | " for t in range(rows.size):\n",
315 | " i=rows[t]\n",
316 | " j = cols[t]\n",
317 | " if (i-1) >= 0: \n",
318 | " StockPrices.iloc[i,j]= StockPrices.iloc[i-1, j].copy()\n",
319 | " else:\n",
320 | " print('error')\n",
321 | " return StockPrices"
322 | ]
323 | },
324 | {
325 | "cell_type": "markdown",
326 | "metadata": {},
327 | "source": [
328 | "The Python code to undertake missing values filling for the dataset shown in Fig. 2.2 is shown below."
329 | ]
330 | },
331 | {
332 | "cell_type": "code",
333 | "execution_count": 13,
334 | "metadata": {},
335 | "outputs": [
336 | {
337 | "name": "stdout",
338 | "output_type": "stream",
339 | "text": [
340 | "Asset Labels: ['JINDAL SAW LTD', 'BAJAJ HINDUSTHAN LIMITED', 'LAKSHMI MACHINE WORKS LTD', 'GUJARAT MINERAL DEV CORP LTD', 'MOSER BAER INDIA LTD']\n",
341 | "Fill missing values...\n",
342 | "Filling missing values completed!\n",
343 | "\n",
344 | " JINDAL SAW LTD BAJAJ HINDUSTHAN LIMITED LAKSHMI MACHINE WORKS LTD \\\n",
345 | "0 60.85 5.10 69.00 \n",
346 | "1 60.70 5.10 70.00 \n",
347 | "2 59.50 5.10 70.00 \n",
348 | "3 58.80 5.06 69.50 \n",
349 | "4 57.25 4.92 66.49 \n",
350 | "5 57.05 5.25 69.90 \n",
351 | "6 62.35 5.25 70.00 \n",
352 | "7 66.55 4.86 70.00 \n",
353 | "8 68.50 4.86 70.50 \n",
354 | "9 69.65 4.86 70.50 \n",
355 | "10 65.75 4.86 70.00 \n",
356 | "\n",
357 | " GUJARAT MINERAL DEV CORP LTD MOSER BAER INDIA LTD \n",
358 | "0 4.80 86.08 \n",
359 | "1 4.75 86.55 \n",
360 | "2 4.68 86.87 \n",
361 | "3 4.73 86.67 \n",
362 | "4 4.83 86.65 \n",
363 | "5 5.07 84.27 \n",
364 | "6 5.01 82.45 \n",
365 | "7 4.95 82.92 \n",
366 | "8 4.96 83.62 \n",
367 | "9 4.95 84.22 \n",
368 | "10 4.94 83.20 \n"
369 | ]
370 | }
371 | ],
372 | "source": [
373 | "#filling missing values of stock prices dataset\n",
374 | "\n",
375 | "#dependencies\n",
376 | "import numpy as np\n",
377 | "import pandas as pd\n",
378 | "\n",
379 | "#input dataset and the dimensions of the cleaned dataset\n",
380 | "StockFileName = 'Lesson2MissingValBSE200.csv'\n",
381 | "Rows = 11 #excluding header\n",
382 | "Columns = 5 #excluding date\n",
383 | "\n",
384 | "#read stock prices from the dataset as a pandas dataframe\n",
385 | "df = pd.read_csv(StockFileName, nrows= Rows)\n",
386 | "StockData = df.iloc[0:, 1:]\n",
387 | "\n",
388 | "#extract asset labels\n",
389 | "assetLabels = df.columns[1:Columns+1].tolist()\n",
390 | "print('Asset Labels:',assetLabels)\n",
391 | "\n",
392 | "#impute missing data with previous day's trading price\n",
393 | "stockDataClean = FillMissingValues(StockData)\n",
394 | "print('Filling missing values completed!\\n')\n",
395 | "print(stockDataClean)\n"
396 | ]
397 | },
398 | {
399 | "cell_type": "markdown",
400 | "metadata": {},
401 | "source": [
402 | "The two functions **EmptyRowsElimination** and **FillMissingValues** have been independently demonstrated over two live datasets only for purposes of illustration. In practice, needless to say, every stock price dataset will have to undergo the two cleaning operations in a sequence, to render them fit for models that will work on critical investment decision making using these historical datasets. "
403 | ]
404 | },
405 | {
406 | "cell_type": "markdown",
407 | "metadata": {},
408 | "source": [
409 | "## Suggested further reading"
410 | ]
411 | },
412 | {
413 | "cell_type": "markdown",
414 | "metadata": {},
415 | "source": [
416 | "There are innumerable books and blogs that have been written on Data Wrangling or more familiarly Data Pre-processing. It is therefore left to the reader's choice to select those that cater to their individual needs. \n",
417 | "\n",
418 | "**scikit-learn** is a Python Library built on **NumPy**, **SciPy** and **matplotlib**, and comprises a repertoire of tools to work on **machine learning**. The package **sklearn.preprocessing** in scikit-learn, provides numerous utility functions and transformers to help undertake data preprocessing. "
419 | ]
420 | },
421 | {
422 | "cell_type": "markdown",
423 | "metadata": {},
424 | "source": [
425 | "#### Next .....Lesson 3: Heuristic Portfolio Selection\n"
426 | ]
427 | },
428 | {
429 | "cell_type": "markdown",
430 | "metadata": {},
431 | "source": [
432 | ""
433 | ]
434 | },
435 | {
436 | "cell_type": "code",
437 | "execution_count": null,
438 | "metadata": {},
439 | "outputs": [],
440 | "source": []
441 | }
442 | ],
443 | "metadata": {
444 | "kernelspec": {
445 | "display_name": "Python 3",
446 | "language": "python",
447 | "name": "python3"
448 | },
449 | "language_info": {
450 | "codemirror_mode": {
451 | "name": "ipython",
452 | "version": 3
453 | },
454 | "file_extension": ".py",
455 | "mimetype": "text/x-python",
456 | "name": "python",
457 | "nbconvert_exporter": "python",
458 | "pygments_lexer": "ipython3",
459 | "version": "3.7.3"
460 | }
461 | },
462 | "nbformat": 4,
463 | "nbformat_minor": 2
464 | }
465 |
--------------------------------------------------------------------------------
/Lesson3_HeuristicPortfolioSelection/Lesson3Eqn3_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaiViji/PythonFinance-PortfolioOptimization/2bb0ae9f32f44ff66302ea33f53c02e746583a05/Lesson3_HeuristicPortfolioSelection/Lesson3Eqn3_1.png
--------------------------------------------------------------------------------
/Lesson3_HeuristicPortfolioSelection/Lesson3ExitTailImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaiViji/PythonFinance-PortfolioOptimization/2bb0ae9f32f44ff66302ea33f53c02e746583a05/Lesson3_HeuristicPortfolioSelection/Lesson3ExitTailImage.png
--------------------------------------------------------------------------------
/Lesson3_HeuristicPortfolioSelection/Lesson3Fig3_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaiViji/PythonFinance-PortfolioOptimization/2bb0ae9f32f44ff66302ea33f53c02e746583a05/Lesson3_HeuristicPortfolioSelection/Lesson3Fig3_1.png
--------------------------------------------------------------------------------
/Lesson3_HeuristicPortfolioSelection/Lesson3Fig3_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaiViji/PythonFinance-PortfolioOptimization/2bb0ae9f32f44ff66302ea33f53c02e746583a05/Lesson3_HeuristicPortfolioSelection/Lesson3Fig3_2.png
--------------------------------------------------------------------------------
/Lesson3_HeuristicPortfolioSelection/Lesson3Fig3_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaiViji/PythonFinance-PortfolioOptimization/2bb0ae9f32f44ff66302ea33f53c02e746583a05/Lesson3_HeuristicPortfolioSelection/Lesson3Fig3_3.png
--------------------------------------------------------------------------------
/Lesson3_HeuristicPortfolioSelection/Lesson3Fig3_4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaiViji/PythonFinance-PortfolioOptimization/2bb0ae9f32f44ff66302ea33f53c02e746583a05/Lesson3_HeuristicPortfolioSelection/Lesson3Fig3_4.png
--------------------------------------------------------------------------------
/Lesson3_HeuristicPortfolioSelection/Lesson3Fig3_5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaiViji/PythonFinance-PortfolioOptimization/2bb0ae9f32f44ff66302ea33f53c02e746583a05/Lesson3_HeuristicPortfolioSelection/Lesson3Fig3_5.png
--------------------------------------------------------------------------------
/Lesson3_HeuristicPortfolioSelection/Lesson3GoalHeaderImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaiViji/PythonFinance-PortfolioOptimization/2bb0ae9f32f44ff66302ea33f53c02e746583a05/Lesson3_HeuristicPortfolioSelection/Lesson3GoalHeaderImage.png
--------------------------------------------------------------------------------
/Lesson3_HeuristicPortfolioSelection/Lesson3_MainContent.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "### Lesson 3\n"
8 | ]
9 | },
10 | {
11 | "cell_type": "markdown",
12 | "metadata": {},
13 | "source": [
14 | "# Heuristic Portfolio Selection "
15 | ]
16 | },
17 | {
18 | "cell_type": "markdown",
19 | "metadata": {},
20 | "source": [
21 | ""
22 | ]
23 | },
24 | {
25 | "cell_type": "markdown",
26 | "metadata": {},
27 | "source": [
28 | "## 3.1 Introduction"
29 | ]
30 | },
31 | {
32 | "cell_type": "markdown",
33 | "metadata": {},
34 | "source": [
35 | "The universe of stocks, both local and global, can truly baffle investors who wish to make a modest selection of stocks for their portfolios. The vastness of the choices and the diverse behavioural characteristics of each of these stocks with respect to itself and to one another, can render a prudent selection of stocks to be a daunting task indeed! \n",
36 | " \n",
37 | "Fig. 3.1 illustrates an investor's perception of the challenging task of portfolio selection. "
38 | ]
39 | },
40 | {
41 | "attachments": {},
42 | "cell_type": "markdown",
43 | "metadata": {},
44 | "source": [
45 | "\n",
46 | "#### Fig. 3.1 The challenging task of Portfolio Selection - an investor's perception "
47 | ]
48 | },
49 | {
50 | "cell_type": "markdown",
51 | "metadata": {},
52 | "source": [
53 | "Also, there is this adage, \"**never put all your eggs in the same basket**\"! So, how would an investor know that the stocks picked do not perform similarly, warranting a risk when all stocks react similarly to market events thereby impacting the performance of the portfolio? "
54 | ]
55 | },
56 | {
57 | "cell_type": "markdown",
58 | "metadata": {},
59 | "source": [
60 | "There are several answers available to these questions independently and collectively from traditional investment finance, but a commonly agreed upon answer is **Diversification**. \n",
61 | "Diversification involves investments in different assets or asset classes or markets. A portfolio that comprises such a diversified set of securities can go a long way in mitigating risk since the securities would react differently to market events. \n",
62 | " "
63 | ]
64 | },
65 | {
66 | "cell_type": "markdown",
67 | "metadata": {},
68 | "source": [
69 | "The choice of assets in an investor's portfolio reflects the investor's *risk appetite* or the willingness to take risk. It is an established investment principle that investments giving high returns are always associated with a probability of high risks and those that are less risky are associated with a probability of low returns. \n",
70 | " \n",
71 | "Therefore, while *risk averse* investors, as a matter of fact, would choose to invest a larger share of their capital in risk-free instruments - government bonds, for example, which are notionally risk-free, *risk-seeking* investors on the other hand, would choose to invest a lion's share of their capital in risky instruments - equities or currencies or commodities, for example, which are prone to yield quick and high returns and therefore are inherently risky. \n",
72 | " \n",
73 | "Investors therefore, may hold portfolios constituted by a mix of securities that can render the portfolio investments to range from *less risky* through *moderately risky* to *highly risky*, as measured by a \"riskometer\"."
74 | ]
75 | },
76 | {
77 | "cell_type": "markdown",
78 | "metadata": {},
79 | "source": [
80 | "Fig. 3.2 graphically illustrates some investors' portfolio \"doughnuts\" with the investors' risk appetites and approach to investments symbolically described by the icons superscribed over the investor labels. Thus, Investor C seems to be *risk averse* considering the larger investments made in bonds, which are risk-free, when compared to equities which are generally risky. Investor B turns out to be the opposite owning a *high risk* portfolio, that invests a lion's share of the capital in equities. Investor D and Investor A, both owning chequered portfolios that invest lesser in bonds and more on other securities, could be deemed to be holding *moderately high* and *high risk* portfolios, respectively. "
81 | ]
82 | },
83 | {
84 | "attachments": {},
85 | "cell_type": "markdown",
86 | "metadata": {},
87 | "source": [
88 | "\n",
89 | "#### Fig. 3.2 Portfolio selection vs Investors' risk appetites "
90 | ]
91 | },
92 | {
93 | "cell_type": "markdown",
94 | "metadata": {},
95 | "source": [
96 | "## 3.2 Diversification Index"
97 | ]
98 | },
99 | {
100 | "cell_type": "markdown",
101 | "metadata": {},
102 | "source": [
103 | "A **Diversification Index** quantifies diversification. There are several diversification indices discussed in the literature. **Diversification Ratio** proposed and patented by Yves Choueifaty in 2008 [CHO 08, CHO 13], is a diversification index of recent origin, built on the inter-dependence between assets of a portfolio. Diversification Ratio is the *ratio of the weighted sum of individual asset volatilities to the portfolio's volatility*. "
104 | ]
105 | },
106 | {
107 | "cell_type": "markdown",
108 | "metadata": {},
109 | "source": [
110 | "Let N be the number of assets in the portfolio spanning different asset classes or belonging to a specific class. Let $(\\bar{w}=(w_1,w_2,...w_N) )$ be the weights or the proportion of capital to be invested in individual assets in the portfolio and $\\bar{w}'$ its transpose. Let $(\\bar{\\sigma}=(\\sigma_1,\\sigma_2,...\\sigma_N))$ be the standard deviations of returns on the assets and _V_, the variance-covariance matrix of returns on the assets. The Diversification Ratio of a portfolio is given as follows: "
111 | ]
112 | },
113 | {
114 | "cell_type": "markdown",
115 | "metadata": {},
116 | "source": [
117 | "\n",
118 | "..........(3.1)
\n"
119 | ]
120 | },
121 | {
122 | "cell_type": "markdown",
123 | "metadata": {},
124 | "source": [
125 | "A portfolio that is *most diversified* would yield the *maximal Diversification Ratio*. "
126 | ]
127 | },
128 | {
129 | "cell_type": "markdown",
130 | "metadata": {},
131 | "source": [
132 | "## 3.3 Clustering"
133 | ]
134 | },
135 | {
136 | "cell_type": "markdown",
137 | "metadata": {},
138 | "source": [
139 | " A one-shot solution to ensure prudent selection of assets from a stock universe, which will ensure benefits of Diversification is **Clustering** or **Cluster Analysis**. Clustering deals with the task of grouping a set of physical or abstract objects into classes such that objects within a class exhibit close **similarity** to one another, while simultaneously expressing a strong **dissimilarity** to objects with other classes. "
140 | ]
141 | },
142 | {
143 | "cell_type": "markdown",
144 | "metadata": {},
145 | "source": [
146 | "Fig. 3.3 graphically illustrates clustering of objects represented by points, into three clusters. It can be easily seen that those points that are in proximity to one another have grouped themselves into a cluster. Marking the points in each of these clusters with different symbols, visibly shows within-class similarity of objects in the cluster and out-of-class dissimilarity to objects in other clusters. \n",
147 | " \n",
148 | "Thus, clustering exploits the characteristic of objects with similar features or behaviour, gravitating towards one another, while moving away or repelling the influence of objects which are dissimilar to them in features or behaviour, thereby satisfying the adage, \"*birds of a feather flock together*\". "
149 | ]
150 | },
151 | {
152 | "attachments": {},
153 | "cell_type": "markdown",
154 | "metadata": {},
155 | "source": [
156 | "\n",
157 | "#### Fig. 3.3 Graphical illustration of clustering objects represented by points, into groups "
158 | ]
159 | },
160 | {
161 | "cell_type": "markdown",
162 | "metadata": {},
163 | "source": [
164 | "Several clustering methods have been explored. **$k$-means clustering** is a cluster analysis technique which groups $N$ objects into ***k*** clusters, where $k$ is an input decided by the user. In the case of portfolio selection, if $N$ were to indicate the size of the stock universe, $k$ could indicate the portfolio size and the investor can use the input $k$ to his or her advantage to make a choice of small portfolios (typically $k\\le30$) or a large portfolio (typically $k >30$)."
165 | ]
166 | },
167 | {
168 | "cell_type": "markdown",
169 | "metadata": {},
170 | "source": [
171 | "## 3.4 Case Study"
172 | ]
173 | },
174 | {
175 | "cell_type": "markdown",
176 | "metadata": {},
177 | "source": [
178 | "Let us restrict our discussion to selection of equity stocks, after all any asset allocation plan involves significant portion of the capital being invested in equities. "
179 | ]
180 | },
181 | {
182 | "cell_type": "markdown",
183 | "metadata": {},
184 | "source": [
185 | "We shall consider a \"mini\" stock universe of 29 equity stocks of Dow Jones Industrial Average (DJIA) Index viz., Apple (AAPL), American Express (AXP), Boeing (BA), Caterpillar (CAT), Cisco Systems (CSCO), \tChevron (CVX), \tWalt Disney (DIS), Goldman Sachs (GS), \tThe Home Depot (HD), \tIBM (IBM), \tIntel (INTC), Johnson & Johnson\t(JNJ), \tJP Morgan Chase (JPM), Coca-Cola (KO), \tMcDonald's (MCD), 3M(MMM), Merck & Co\t(MRK), Microsoft (MSFT), Nike\t(NKE), \tPfizer (PFE), Procter & Gamble\t(PG), \tTravelers (TRV), United Health Group\t(UNH), United Technologies\t(UTX), Verizon\t(V), Verizon (VZ), Walgreens Boots Alliance (WBA), Walmart (WMT), \tExxon Mobil (XOM). \n",
186 | "\n",
187 | " \n",
188 | "The data set considered is from April 11, 2014 to April 11, 2019. Fig. 3.4 illustrates a snapshot of the DJIA dataset."
189 | ]
190 | },
191 | {
192 | "attachments": {},
193 | "cell_type": "markdown",
194 | "metadata": {},
195 | "source": [
196 | "\n",
197 | "#### Fig. 3.4 Snapshot of the DJIA dataset "
198 | ]
199 | },
200 | {
201 | "cell_type": "markdown",
202 | "metadata": {},
203 | "source": [
204 | "An investor desires to invest in 15 stocks from this \"mini\" universe. The following questions arise: \n",
205 | "\n",
206 | " > How can the investor decide on which combination of assets among the 29 stocks, is the best? \n",
207 | " > How can diversification be ensured, when the assets belong to different sectors and therefore behave differently under varying market conditions? \n",
208 | " \n",
209 | " $k$-means clustering provides solutions to both questions at once. The following steps and the companion Python code illustrate this **heuristic portfolio selection process**. The Python code employs NumPy, Pandas and scikit-learn to effect portfolio selection using $k$-means clustering."
210 | ]
211 | },
212 | {
213 | "cell_type": "markdown",
214 | "metadata": {},
215 | "source": [
216 | "**Step 1:** ***Undertake data wrangling of the original stock dataset to keep it fit for further processing***. \n",
217 | "\n",
218 | "(Refer *Lesson2 Some glimpses of financial data wrangling* to learn about aspects of financial data wrangling). \n",
219 | "We assume that the DJIA dataset for the \"mini\" universe of 29 stocks is already cleaned and the Python code shown below reads the CSV file concerned. **clusters = 15** represents the portfolio size desired by the investor."
220 | ]
221 | },
222 | {
223 | "cell_type": "code",
224 | "execution_count": 15,
225 | "metadata": {},
226 | "outputs": [
227 | {
228 | "name": "stdout",
229 | "output_type": "stream",
230 | "text": [
231 | "['AAPL', 'AXP', 'BA', 'CAT', 'CSCO', 'CVX', 'DIS', 'GS', 'HD', 'IBM', 'INTC', 'JNJ', 'JPM', 'KO', 'MCD', 'MMM', 'MRK', 'MSFT', 'NKE', 'PFE', 'PG', 'TRV', 'UNH', 'UTX', 'V', 'VZ', 'WBA', 'WMT', 'XOM']\n",
232 | "1259 29\n",
233 | "[[ 74.23 84.54 122.07 ... 64.26 76.5 96.72 ]\n",
234 | " [ 74.52571 85.5 123.25 ... 65.67 77.38 97.86 ]\n",
235 | " [ 73.99429 86.04 124.27 ... 66.01 76.88 98.68 ]\n",
236 | " ...\n",
237 | " [199.5 109.85 369.04001 ... 54.5 98.69 81.93 ]\n",
238 | " [200.61999 110.16 364.94 ... 54.51 99.6 81.56 ]\n",
239 | " [198.95 109.85 370.16 ... 53.44 100.8 81.95 ]]\n"
240 | ]
241 | }
242 | ],
243 | "source": [
244 | "#read stock prices from a cleaned DJIA dataset \n",
245 | "\n",
246 | "#Dependencies \n",
247 | "import numpy as np\n",
248 | "import pandas as pd\n",
249 | "from sklearn.cluster import KMeans \n",
250 | "\n",
251 | "#input stock prices data set \n",
252 | "stockFileName = 'DJIA_Apr112014_Apr112019.csv'\n",
253 | "originalRows = 1259 #excluding header\n",
254 | "originalColumns = 29 #excluding date\n",
255 | "clusters = 15\n",
256 | "\n",
257 | "#read stock dataset into a dataframe \n",
258 | "df = pd.read_csv(stockFileName, nrows= originalRows)\n",
259 | "\n",
260 | "#extract asset labels\n",
261 | "assetLabels = df.columns[1:originalColumns+1].tolist()\n",
262 | "print(assetLabels)\n",
263 | "\n",
264 | "#extract stock prices excluding header and trading dates\n",
265 | "dfStockPrices = df.iloc[0:, 1:]\n",
266 | "\n",
267 | "#store stock prices as an array\n",
268 | "arStockPrices = np.asarray(dfStockPrices)\n",
269 | "[rows, cols]= arStockPrices.shape\n",
270 | "print(rows, cols)\n",
271 | "print(arStockPrices)\n"
272 | ]
273 | },
274 | {
275 | "cell_type": "markdown",
276 | "metadata": {},
277 | "source": [
278 | "**Step 2:** ***Compute asset returns of the stocks*** \n",
279 | "\n",
280 | "(Refer *Lesson 1 Fundamentals of Risk and Return of a Portfolio* to know about stock returns computing) \n",
281 | "Function **StockReturnsComputing** computes the daily return of stocks in the DJIA index and stores it in the array **arReturns**, as shown in the Python code fragment given below."
282 | ]
283 | },
284 | {
285 | "cell_type": "code",
286 | "execution_count": 16,
287 | "metadata": {},
288 | "outputs": [],
289 | "source": [
290 | "#function for Stock Returns computing \n",
291 | "def StockReturnsComputing(StockPrice, Rows, Columns):\n",
292 | " \n",
293 | " import numpy as np\n",
294 | " \n",
295 | " StockReturn = np.zeros([Rows-1, Columns])\n",
296 | " for j in range(Columns): # j: Assets\n",
297 | " for i in range(Rows-1): #i: Daily Prices\n",
298 | " StockReturn[i,j]=((StockPrice[i+1, j]-StockPrice[i,j])/StockPrice[i,j])\n",
299 | "\n",
300 | " return StockReturn\n"
301 | ]
302 | },
303 | {
304 | "cell_type": "code",
305 | "execution_count": 17,
306 | "metadata": {},
307 | "outputs": [
308 | {
309 | "name": "stdout",
310 | "output_type": "stream",
311 | "text": [
312 | "Size of the array of daily returns of stocks:\n",
313 | " (1258, 29)\n",
314 | "Array of daily returns of stocks\n",
315 | " [[ 0.00398 0.01136 0.00967 ... 0.02194 0.0115 0.01179]\n",
316 | " [-0.00713 0.00632 0.00828 ... 0.00518 -0.00646 0.00838]\n",
317 | " [ 0.00203 0.01581 0.01424 ... 0.00227 0.00442 0.01277]\n",
318 | " ...\n",
319 | " [-0.003 -0.00768 -0.01463 ... -0.01017 -0.00544 -0.01289]\n",
320 | " [ 0.00561 0.00282 -0.01111 ... 0.00018 0.00922 -0.00452]\n",
321 | " [-0.00832 -0.00281 0.0143 ... -0.01963 0.01205 0.00478]]\n"
322 | ]
323 | }
324 | ],
325 | "source": [
326 | "#compute daily returns of all stocks in the mini universe\n",
327 | "arReturns = StockReturnsComputing(arStockPrices, rows, cols)\n",
328 | "print('Size of the array of daily returns of stocks:\\n', arReturns.shape)\n",
329 | "print('Array of daily returns of stocks\\n', arReturns)"
330 | ]
331 | },
332 | {
333 | "cell_type": "markdown",
334 | "metadata": {},
335 | "source": [
336 | "**Step 3:** ***Compute mean returns and variance-covariance matrix of returns*** \n",
337 | "\n",
338 | "(Refer *Lesson 1 Fundamentals of Risk and Return of a Portfolio* to know about computing the mean returns and variance-covariance matrix of stock returns) \n",
339 | "**meanReturns** and **covReturns** store the outputs of the respective computations. "
340 | ]
341 | },
342 | {
343 | "cell_type": "code",
344 | "execution_count": 18,
345 | "metadata": {},
346 | "outputs": [
347 | {
348 | "name": "stdout",
349 | "output_type": "stream",
350 | "text": [
351 | "Mean returns:\n",
352 | " [ 0.0009 0.00029 0.001 0.00039 0.00081 0.00016 0.0004 0.00033\n",
353 | " 0.00085 -0.00016 0.00073 0.00032 0.0006 0.00019 0.00057 0.00044\n",
354 | " 0.00036 0.001 0.0008 0.00034 0.00025 0.00043 0.00095 0.00019\n",
355 | " 0.00101 0.00023 -0.00002 0.00029 -0.00006]\n",
356 | "Size of Variance-Covariance matrix of returns:\n",
357 | " (29, 29)\n",
358 | "Variance-Covariance matrix of returns:\n",
359 | " [[0.00024 0.00007 0.0001 0.0001 0.0001 0.00007 0.00007 0.0001 0.00007\n",
360 | " 0.00007 0.00011 0.00005 0.00008 0.00003 0.00005 0.00007 0.00005 0.00012\n",
361 | " 0.00008 0.00005 0.00004 0.00005 0.00008 0.00007 0.0001 0.00003 0.00007\n",
362 | " 0.00004 0.00006]\n",
363 | " [0.00007 0.00016 0.00008 0.0001 0.00007 0.00006 0.00006 0.00011 0.00007\n",
364 | " 0.00007 0.00007 0.00005 0.0001 0.00003 0.00004 0.00007 0.00006 0.00008\n",
365 | " 0.00007 0.00005 0.00003 0.00005 0.00007 0.00007 0.00008 0.00003 0.00007\n",
366 | " 0.00004 0.00005]\n",
367 | " [0.0001 0.00008 0.00023 0.00013 0.00009 0.00008 0.00007 0.00011 0.00007\n",
368 | " 0.00008 0.0001 0.00006 0.0001 0.00004 0.00005 0.00009 0.00006 0.00009\n",
369 | " 0.00008 0.00005 0.00004 0.00006 0.00007 0.00009 0.00009 0.00004 0.00007\n",
370 | " 0.00005 0.00007]\n",
371 | " [0.0001 0.0001 0.00013 0.00027 0.0001 0.00012 0.00007 0.00013 0.00008\n",
372 | " 0.00009 0.00012 0.00005 0.00012 0.00004 0.00005 0.00011 0.00006 0.00011\n",
373 | " 0.00008 0.00006 0.00004 0.00007 0.00007 0.0001 0.0001 0.00004 0.00007\n",
374 | " 0.00004 0.00011]\n",
375 | " [0.0001 0.00007 0.00009 0.0001 0.00018 0.00008 0.00007 0.00009 0.00007\n",
376 | " 0.00008 0.00012 0.00006 0.00009 0.00004 0.00005 0.00008 0.00006 0.00011\n",
377 | " 0.00008 0.00006 0.00004 0.00006 0.00007 0.00007 0.00009 0.00005 0.00007\n",
378 | " 0.00006 0.00007]\n",
379 | " [0.00007 0.00006 0.00008 0.00012 0.00008 0.00019 0.00006 0.00009 0.00006\n",
380 | " 0.00007 0.00008 0.00005 0.00009 0.00004 0.00004 0.00007 0.00006 0.00008\n",
381 | " 0.00005 0.00005 0.00004 0.00006 0.00006 0.00006 0.00007 0.00005 0.00006\n",
382 | " 0.00004 0.00013]\n",
383 | " [0.00007 0.00006 0.00007 0.00007 0.00007 0.00006 0.00014 0.00008 0.00006\n",
384 | " 0.00006 0.00007 0.00004 0.00007 0.00003 0.00004 0.00006 0.00005 0.00007\n",
385 | " 0.00007 0.00005 0.00004 0.00005 0.00006 0.00005 0.00007 0.00004 0.00006\n",
386 | " 0.00004 0.00006]\n",
387 | " [0.0001 0.00011 0.00011 0.00013 0.00009 0.00009 0.00008 0.00021 0.00008\n",
388 | " 0.00008 0.0001 0.00005 0.00016 0.00003 0.00005 0.00008 0.00007 0.0001\n",
389 | " 0.00008 0.00006 0.00004 0.00008 0.00008 0.00008 0.0001 0.00004 0.00008\n",
390 | " 0.00004 0.00008]\n",
391 | " [0.00007 0.00007 0.00007 0.00008 0.00007 0.00006 0.00006 0.00008 0.00014\n",
392 | " 0.00006 0.00007 0.00005 0.00008 0.00003 0.00005 0.00006 0.00005 0.00008\n",
393 | " 0.00008 0.00005 0.00004 0.00005 0.00007 0.00006 0.00008 0.00004 0.00007\n",
394 | " 0.00006 0.00005]\n",
395 | " [0.00007 0.00007 0.00008 0.00009 0.00008 0.00007 0.00006 0.00008 0.00006\n",
396 | " 0.00016 0.00008 0.00005 0.00008 0.00004 0.00004 0.00007 0.00006 0.00009\n",
397 | " 0.00006 0.00005 0.00004 0.00005 0.00006 0.00007 0.00008 0.00004 0.00005\n",
398 | " 0.00004 0.00006]\n",
399 | " [0.00011 0.00007 0.0001 0.00012 0.00012 0.00008 0.00007 0.0001 0.00007\n",
400 | " 0.00008 0.00025 0.00006 0.00009 0.00004 0.00004 0.00009 0.00006 0.00013\n",
401 | " 0.00007 0.00006 0.00004 0.00006 0.00007 0.00008 0.00009 0.00005 0.00007\n",
402 | " 0.00005 0.00007]\n",
403 | " [0.00005 0.00005 0.00006 0.00005 0.00006 0.00005 0.00004 0.00005 0.00005\n",
404 | " 0.00005 0.00006 0.0001 0.00005 0.00004 0.00004 0.00006 0.00006 0.00006\n",
405 | " 0.00005 0.00006 0.00004 0.00005 0.00006 0.00005 0.00005 0.00004 0.00006\n",
406 | " 0.00004 0.00005]\n",
407 | " [0.00008 0.0001 0.0001 0.00012 0.00009 0.00009 0.00007 0.00016 0.00008\n",
408 | " 0.00008 0.00009 0.00005 0.00017 0.00003 0.00005 0.00008 0.00007 0.00009\n",
409 | " 0.00007 0.00006 0.00004 0.00008 0.00008 0.00008 0.00009 0.00004 0.00007\n",
410 | " 0.00004 0.00008]\n",
411 | " [0.00003 0.00003 0.00004 0.00004 0.00004 0.00004 0.00003 0.00003 0.00003\n",
412 | " 0.00004 0.00004 0.00004 0.00003 0.00008 0.00004 0.00004 0.00004 0.00004\n",
413 | " 0.00004 0.00003 0.00005 0.00004 0.00003 0.00003 0.00004 0.00004 0.00004\n",
414 | " 0.00003 0.00003]\n",
415 | " [0.00005 0.00004 0.00005 0.00005 0.00005 0.00004 0.00004 0.00005 0.00005\n",
416 | " 0.00004 0.00004 0.00004 0.00005 0.00004 0.00011 0.00004 0.00004 0.00006\n",
417 | " 0.00005 0.00003 0.00004 0.00004 0.00004 0.00004 0.00005 0.00004 0.00004\n",
418 | " 0.00004 0.00004]\n",
419 | " [0.00007 0.00007 0.00009 0.00011 0.00008 0.00007 0.00006 0.00008 0.00006\n",
420 | " 0.00007 0.00009 0.00006 0.00008 0.00004 0.00004 0.00012 0.00006 0.00008\n",
421 | " 0.00006 0.00006 0.00004 0.00006 0.00006 0.00007 0.00007 0.00004 0.00006\n",
422 | " 0.00004 0.00006]\n",
423 | " [0.00005 0.00006 0.00006 0.00006 0.00006 0.00006 0.00005 0.00007 0.00005\n",
424 | " 0.00006 0.00006 0.00006 0.00007 0.00004 0.00004 0.00006 0.00015 0.00006\n",
425 | " 0.00005 0.00008 0.00004 0.00005 0.00006 0.00005 0.00006 0.00005 0.00006\n",
426 | " 0.00005 0.00006]\n",
427 | " [0.00012 0.00008 0.00009 0.00011 0.00011 0.00008 0.00007 0.0001 0.00008\n",
428 | " 0.00009 0.00013 0.00006 0.00009 0.00004 0.00006 0.00008 0.00006 0.00021\n",
429 | " 0.00008 0.00007 0.00005 0.00006 0.00008 0.00008 0.00012 0.00005 0.00008\n",
430 | " 0.00005 0.00007]\n",
431 | " [0.00008 0.00007 0.00008 0.00008 0.00008 0.00005 0.00007 0.00008 0.00008\n",
432 | " 0.00006 0.00007 0.00005 0.00007 0.00004 0.00005 0.00006 0.00005 0.00008\n",
433 | " 0.00022 0.00005 0.00004 0.00005 0.00007 0.00006 0.00008 0.00004 0.00007\n",
434 | " 0.00005 0.00005]\n",
435 | " [0.00005 0.00005 0.00005 0.00006 0.00006 0.00005 0.00005 0.00006 0.00005\n",
436 | " 0.00005 0.00006 0.00006 0.00006 0.00003 0.00003 0.00006 0.00008 0.00007\n",
437 | " 0.00005 0.00012 0.00004 0.00005 0.00007 0.00005 0.00006 0.00004 0.00006\n",
438 | " 0.00004 0.00005]\n",
439 | " [0.00004 0.00003 0.00004 0.00004 0.00004 0.00004 0.00004 0.00004 0.00004\n",
440 | " 0.00004 0.00004 0.00004 0.00004 0.00005 0.00004 0.00004 0.00004 0.00005\n",
441 | " 0.00004 0.00004 0.00009 0.00004 0.00004 0.00004 0.00004 0.00004 0.00004\n",
442 | " 0.00004 0.00004]\n",
443 | " [0.00005 0.00005 0.00006 0.00007 0.00006 0.00006 0.00005 0.00008 0.00005\n",
444 | " 0.00005 0.00006 0.00005 0.00008 0.00004 0.00004 0.00006 0.00005 0.00006\n",
445 | " 0.00005 0.00005 0.00004 0.00011 0.00006 0.00006 0.00006 0.00004 0.00006\n",
446 | " 0.00004 0.00005]\n",
447 | " [0.00008 0.00007 0.00007 0.00007 0.00007 0.00006 0.00006 0.00008 0.00007\n",
448 | " 0.00006 0.00007 0.00006 0.00008 0.00003 0.00004 0.00006 0.00006 0.00008\n",
449 | " 0.00007 0.00007 0.00004 0.00006 0.00017 0.00006 0.00007 0.00004 0.00008\n",
450 | " 0.00005 0.00005]\n",
451 | " [0.00007 0.00007 0.00009 0.0001 0.00007 0.00006 0.00005 0.00008 0.00006\n",
452 | " 0.00007 0.00008 0.00005 0.00008 0.00003 0.00004 0.00007 0.00005 0.00008\n",
453 | " 0.00006 0.00005 0.00004 0.00006 0.00006 0.00013 0.00007 0.00004 0.00006\n",
454 | " 0.00004 0.00006]\n",
455 | " [0.0001 0.00008 0.00009 0.0001 0.00009 0.00007 0.00007 0.0001 0.00008\n",
456 | " 0.00008 0.00009 0.00005 0.00009 0.00004 0.00005 0.00007 0.00006 0.00012\n",
457 | " 0.00008 0.00006 0.00004 0.00006 0.00007 0.00007 0.00017 0.00003 0.00007\n",
458 | " 0.00004 0.00006]\n",
459 | " [0.00003 0.00003 0.00004 0.00004 0.00005 0.00005 0.00004 0.00004 0.00004\n",
460 | " 0.00004 0.00005 0.00004 0.00004 0.00004 0.00004 0.00004 0.00005 0.00005\n",
461 | " 0.00004 0.00004 0.00004 0.00004 0.00004 0.00004 0.00003 0.00012 0.00005\n",
462 | " 0.00004 0.00004]\n",
463 | " [0.00007 0.00007 0.00007 0.00007 0.00007 0.00006 0.00006 0.00008 0.00007\n",
464 | " 0.00005 0.00007 0.00006 0.00007 0.00004 0.00004 0.00006 0.00006 0.00008\n",
465 | " 0.00007 0.00006 0.00004 0.00006 0.00008 0.00006 0.00007 0.00005 0.00026\n",
466 | " 0.00006 0.00005]\n",
467 | " [0.00004 0.00004 0.00005 0.00004 0.00006 0.00004 0.00004 0.00004 0.00006\n",
468 | " 0.00004 0.00005 0.00004 0.00004 0.00003 0.00004 0.00004 0.00005 0.00005\n",
469 | " 0.00005 0.00004 0.00004 0.00004 0.00005 0.00004 0.00004 0.00004 0.00006\n",
470 | " 0.00015 0.00004]\n",
471 | " [0.00006 0.00005 0.00007 0.00011 0.00007 0.00013 0.00006 0.00008 0.00005\n",
472 | " 0.00006 0.00007 0.00005 0.00008 0.00003 0.00004 0.00006 0.00006 0.00007\n",
473 | " 0.00005 0.00005 0.00004 0.00005 0.00005 0.00006 0.00006 0.00004 0.00005\n",
474 | " 0.00004 0.00014]]\n"
475 | ]
476 | }
477 | ],
478 | "source": [
479 | "#compute mean returns and variance covariance matrix of returns\n",
480 | "meanReturns = np.mean(arReturns, axis = 0)\n",
481 | "print('Mean returns:\\n', meanReturns)\n",
482 | "covReturns = np.cov(arReturns, rowvar=False)\n",
483 | "#set precision for printing results\n",
484 | "np.set_printoptions(precision=5, suppress = True)\n",
485 | "print('Size of Variance-Covariance matrix of returns:\\n', covReturns.shape)\n",
486 | "print('Variance-Covariance matrix of returns:\\n', covReturns)"
487 | ]
488 | },
489 | {
490 | "cell_type": "markdown",
491 | "metadata": {},
492 | "source": [
493 | "**Step 4:** ***Prepare parameters for k-means clustering*** \n",
494 | "\n",
495 | "Every asset $A_i$ is characterized by its mean return and the variance-covariance vector of its returns with those of other assets $A_j$. For *i = j*, it would indicate its own variance of returns. Thus the characteristic vector for asset $A_i$ is given by $\\left[\\mu_i, \\sigma_{i1},\\sigma_{i2},...\\sigma_{ii}, ...\\sigma_{iN}) \\right]$, where $\\mu_i$ indicates the mean return of asset $A_i$ and $\\sigma_{i1},\\sigma_{i2},...\\sigma_{ii}, ...\\sigma_{iN}$ are the variance and covariance of its returns with other assets. It can be seen that $\\left[ \\sigma_{i1},\\sigma_{i2},...\\sigma_{ii}, ...\\sigma_{iN} \\right]$ is nothing but row $i$ of the variance-covariance matrix $V$ of $N$ assets in the stock universe. $\\sigma_{ii}$ which is the variance of the asset return, is the diagonal element of matrix $V$ in row *i*. "
496 | ]
497 | },
498 | {
499 | "cell_type": "markdown",
500 | "metadata": {},
501 | "source": [
502 | "The following Python code shows the gathering of parameters for each of the 29 assets in the stock universe. The parameters are to be provided as inputs to the $k$-means clustering method. Each characteristic vector of the asset comprises 30 components viz., its own mean return as the first element of the vector followed by its covariance/variance of returns with the rest of the 29 assets. Thus **assetParameter** holds the the characteristic vectors of all the 29 assets in the stock universe. "
503 | ]
504 | },
505 | {
506 | "cell_type": "code",
507 | "execution_count": 19,
508 | "metadata": {},
509 | "outputs": [
510 | {
511 | "name": "stdout",
512 | "output_type": "stream",
513 | "text": [
514 | "Size of the asset parameters for clustering:\n",
515 | " (29, 30)\n",
516 | "Asset parameters for clustering:\n",
517 | " [[ 0.0009 0.00024 0.00007 0.0001 0.0001 0.0001 0.00007 0.00007\n",
518 | " 0.0001 0.00007 0.00007 0.00011 0.00005 0.00008 0.00003 0.00005\n",
519 | " 0.00007 0.00005 0.00012 0.00008 0.00005 0.00004 0.00005 0.00008\n",
520 | " 0.00007 0.0001 0.00003 0.00007 0.00004 0.00006]\n",
521 | " [ 0.00029 0.00007 0.00016 0.00008 0.0001 0.00007 0.00006 0.00006\n",
522 | " 0.00011 0.00007 0.00007 0.00007 0.00005 0.0001 0.00003 0.00004\n",
523 | " 0.00007 0.00006 0.00008 0.00007 0.00005 0.00003 0.00005 0.00007\n",
524 | " 0.00007 0.00008 0.00003 0.00007 0.00004 0.00005]\n",
525 | " [ 0.001 0.0001 0.00008 0.00023 0.00013 0.00009 0.00008 0.00007\n",
526 | " 0.00011 0.00007 0.00008 0.0001 0.00006 0.0001 0.00004 0.00005\n",
527 | " 0.00009 0.00006 0.00009 0.00008 0.00005 0.00004 0.00006 0.00007\n",
528 | " 0.00009 0.00009 0.00004 0.00007 0.00005 0.00007]\n",
529 | " [ 0.00039 0.0001 0.0001 0.00013 0.00027 0.0001 0.00012 0.00007\n",
530 | " 0.00013 0.00008 0.00009 0.00012 0.00005 0.00012 0.00004 0.00005\n",
531 | " 0.00011 0.00006 0.00011 0.00008 0.00006 0.00004 0.00007 0.00007\n",
532 | " 0.0001 0.0001 0.00004 0.00007 0.00004 0.00011]\n",
533 | " [ 0.00081 0.0001 0.00007 0.00009 0.0001 0.00018 0.00008 0.00007\n",
534 | " 0.00009 0.00007 0.00008 0.00012 0.00006 0.00009 0.00004 0.00005\n",
535 | " 0.00008 0.00006 0.00011 0.00008 0.00006 0.00004 0.00006 0.00007\n",
536 | " 0.00007 0.00009 0.00005 0.00007 0.00006 0.00007]\n",
537 | " [ 0.00016 0.00007 0.00006 0.00008 0.00012 0.00008 0.00019 0.00006\n",
538 | " 0.00009 0.00006 0.00007 0.00008 0.00005 0.00009 0.00004 0.00004\n",
539 | " 0.00007 0.00006 0.00008 0.00005 0.00005 0.00004 0.00006 0.00006\n",
540 | " 0.00006 0.00007 0.00005 0.00006 0.00004 0.00013]\n",
541 | " [ 0.0004 0.00007 0.00006 0.00007 0.00007 0.00007 0.00006 0.00014\n",
542 | " 0.00008 0.00006 0.00006 0.00007 0.00004 0.00007 0.00003 0.00004\n",
543 | " 0.00006 0.00005 0.00007 0.00007 0.00005 0.00004 0.00005 0.00006\n",
544 | " 0.00005 0.00007 0.00004 0.00006 0.00004 0.00006]\n",
545 | " [ 0.00033 0.0001 0.00011 0.00011 0.00013 0.00009 0.00009 0.00008\n",
546 | " 0.00021 0.00008 0.00008 0.0001 0.00005 0.00016 0.00003 0.00005\n",
547 | " 0.00008 0.00007 0.0001 0.00008 0.00006 0.00004 0.00008 0.00008\n",
548 | " 0.00008 0.0001 0.00004 0.00008 0.00004 0.00008]\n",
549 | " [ 0.00085 0.00007 0.00007 0.00007 0.00008 0.00007 0.00006 0.00006\n",
550 | " 0.00008 0.00014 0.00006 0.00007 0.00005 0.00008 0.00003 0.00005\n",
551 | " 0.00006 0.00005 0.00008 0.00008 0.00005 0.00004 0.00005 0.00007\n",
552 | " 0.00006 0.00008 0.00004 0.00007 0.00006 0.00005]\n",
553 | " [-0.00016 0.00007 0.00007 0.00008 0.00009 0.00008 0.00007 0.00006\n",
554 | " 0.00008 0.00006 0.00016 0.00008 0.00005 0.00008 0.00004 0.00004\n",
555 | " 0.00007 0.00006 0.00009 0.00006 0.00005 0.00004 0.00005 0.00006\n",
556 | " 0.00007 0.00008 0.00004 0.00005 0.00004 0.00006]\n",
557 | " [ 0.00073 0.00011 0.00007 0.0001 0.00012 0.00012 0.00008 0.00007\n",
558 | " 0.0001 0.00007 0.00008 0.00025 0.00006 0.00009 0.00004 0.00004\n",
559 | " 0.00009 0.00006 0.00013 0.00007 0.00006 0.00004 0.00006 0.00007\n",
560 | " 0.00008 0.00009 0.00005 0.00007 0.00005 0.00007]\n",
561 | " [ 0.00032 0.00005 0.00005 0.00006 0.00005 0.00006 0.00005 0.00004\n",
562 | " 0.00005 0.00005 0.00005 0.00006 0.0001 0.00005 0.00004 0.00004\n",
563 | " 0.00006 0.00006 0.00006 0.00005 0.00006 0.00004 0.00005 0.00006\n",
564 | " 0.00005 0.00005 0.00004 0.00006 0.00004 0.00005]\n",
565 | " [ 0.0006 0.00008 0.0001 0.0001 0.00012 0.00009 0.00009 0.00007\n",
566 | " 0.00016 0.00008 0.00008 0.00009 0.00005 0.00017 0.00003 0.00005\n",
567 | " 0.00008 0.00007 0.00009 0.00007 0.00006 0.00004 0.00008 0.00008\n",
568 | " 0.00008 0.00009 0.00004 0.00007 0.00004 0.00008]\n",
569 | " [ 0.00019 0.00003 0.00003 0.00004 0.00004 0.00004 0.00004 0.00003\n",
570 | " 0.00003 0.00003 0.00004 0.00004 0.00004 0.00003 0.00008 0.00004\n",
571 | " 0.00004 0.00004 0.00004 0.00004 0.00003 0.00005 0.00004 0.00003\n",
572 | " 0.00003 0.00004 0.00004 0.00004 0.00003 0.00003]\n",
573 | " [ 0.00057 0.00005 0.00004 0.00005 0.00005 0.00005 0.00004 0.00004\n",
574 | " 0.00005 0.00005 0.00004 0.00004 0.00004 0.00005 0.00004 0.00011\n",
575 | " 0.00004 0.00004 0.00006 0.00005 0.00003 0.00004 0.00004 0.00004\n",
576 | " 0.00004 0.00005 0.00004 0.00004 0.00004 0.00004]\n",
577 | " [ 0.00044 0.00007 0.00007 0.00009 0.00011 0.00008 0.00007 0.00006\n",
578 | " 0.00008 0.00006 0.00007 0.00009 0.00006 0.00008 0.00004 0.00004\n",
579 | " 0.00012 0.00006 0.00008 0.00006 0.00006 0.00004 0.00006 0.00006\n",
580 | " 0.00007 0.00007 0.00004 0.00006 0.00004 0.00006]\n",
581 | " [ 0.00036 0.00005 0.00006 0.00006 0.00006 0.00006 0.00006 0.00005\n",
582 | " 0.00007 0.00005 0.00006 0.00006 0.00006 0.00007 0.00004 0.00004\n",
583 | " 0.00006 0.00015 0.00006 0.00005 0.00008 0.00004 0.00005 0.00006\n",
584 | " 0.00005 0.00006 0.00005 0.00006 0.00005 0.00006]\n",
585 | " [ 0.001 0.00012 0.00008 0.00009 0.00011 0.00011 0.00008 0.00007\n",
586 | " 0.0001 0.00008 0.00009 0.00013 0.00006 0.00009 0.00004 0.00006\n",
587 | " 0.00008 0.00006 0.00021 0.00008 0.00007 0.00005 0.00006 0.00008\n",
588 | " 0.00008 0.00012 0.00005 0.00008 0.00005 0.00007]\n",
589 | " [ 0.0008 0.00008 0.00007 0.00008 0.00008 0.00008 0.00005 0.00007\n",
590 | " 0.00008 0.00008 0.00006 0.00007 0.00005 0.00007 0.00004 0.00005\n",
591 | " 0.00006 0.00005 0.00008 0.00022 0.00005 0.00004 0.00005 0.00007\n",
592 | " 0.00006 0.00008 0.00004 0.00007 0.00005 0.00005]\n",
593 | " [ 0.00034 0.00005 0.00005 0.00005 0.00006 0.00006 0.00005 0.00005\n",
594 | " 0.00006 0.00005 0.00005 0.00006 0.00006 0.00006 0.00003 0.00003\n",
595 | " 0.00006 0.00008 0.00007 0.00005 0.00012 0.00004 0.00005 0.00007\n",
596 | " 0.00005 0.00006 0.00004 0.00006 0.00004 0.00005]\n",
597 | " [ 0.00025 0.00004 0.00003 0.00004 0.00004 0.00004 0.00004 0.00004\n",
598 | " 0.00004 0.00004 0.00004 0.00004 0.00004 0.00004 0.00005 0.00004\n",
599 | " 0.00004 0.00004 0.00005 0.00004 0.00004 0.00009 0.00004 0.00004\n",
600 | " 0.00004 0.00004 0.00004 0.00004 0.00004 0.00004]\n",
601 | " [ 0.00043 0.00005 0.00005 0.00006 0.00007 0.00006 0.00006 0.00005\n",
602 | " 0.00008 0.00005 0.00005 0.00006 0.00005 0.00008 0.00004 0.00004\n",
603 | " 0.00006 0.00005 0.00006 0.00005 0.00005 0.00004 0.00011 0.00006\n",
604 | " 0.00006 0.00006 0.00004 0.00006 0.00004 0.00005]\n",
605 | " [ 0.00095 0.00008 0.00007 0.00007 0.00007 0.00007 0.00006 0.00006\n",
606 | " 0.00008 0.00007 0.00006 0.00007 0.00006 0.00008 0.00003 0.00004\n",
607 | " 0.00006 0.00006 0.00008 0.00007 0.00007 0.00004 0.00006 0.00017\n",
608 | " 0.00006 0.00007 0.00004 0.00008 0.00005 0.00005]\n",
609 | " [ 0.00019 0.00007 0.00007 0.00009 0.0001 0.00007 0.00006 0.00005\n",
610 | " 0.00008 0.00006 0.00007 0.00008 0.00005 0.00008 0.00003 0.00004\n",
611 | " 0.00007 0.00005 0.00008 0.00006 0.00005 0.00004 0.00006 0.00006\n",
612 | " 0.00013 0.00007 0.00004 0.00006 0.00004 0.00006]\n",
613 | " [ 0.00101 0.0001 0.00008 0.00009 0.0001 0.00009 0.00007 0.00007\n",
614 | " 0.0001 0.00008 0.00008 0.00009 0.00005 0.00009 0.00004 0.00005\n",
615 | " 0.00007 0.00006 0.00012 0.00008 0.00006 0.00004 0.00006 0.00007\n",
616 | " 0.00007 0.00017 0.00003 0.00007 0.00004 0.00006]\n",
617 | " [ 0.00023 0.00003 0.00003 0.00004 0.00004 0.00005 0.00005 0.00004\n",
618 | " 0.00004 0.00004 0.00004 0.00005 0.00004 0.00004 0.00004 0.00004\n",
619 | " 0.00004 0.00005 0.00005 0.00004 0.00004 0.00004 0.00004 0.00004\n",
620 | " 0.00004 0.00003 0.00012 0.00005 0.00004 0.00004]\n",
621 | " [-0.00002 0.00007 0.00007 0.00007 0.00007 0.00007 0.00006 0.00006\n",
622 | " 0.00008 0.00007 0.00005 0.00007 0.00006 0.00007 0.00004 0.00004\n",
623 | " 0.00006 0.00006 0.00008 0.00007 0.00006 0.00004 0.00006 0.00008\n",
624 | " 0.00006 0.00007 0.00005 0.00026 0.00006 0.00005]\n",
625 | " [ 0.00029 0.00004 0.00004 0.00005 0.00004 0.00006 0.00004 0.00004\n",
626 | " 0.00004 0.00006 0.00004 0.00005 0.00004 0.00004 0.00003 0.00004\n",
627 | " 0.00004 0.00005 0.00005 0.00005 0.00004 0.00004 0.00004 0.00005\n",
628 | " 0.00004 0.00004 0.00004 0.00006 0.00015 0.00004]\n",
629 | " [-0.00006 0.00006 0.00005 0.00007 0.00011 0.00007 0.00013 0.00006\n",
630 | " 0.00008 0.00005 0.00006 0.00007 0.00005 0.00008 0.00003 0.00004\n",
631 | " 0.00006 0.00006 0.00007 0.00005 0.00005 0.00004 0.00005 0.00005\n",
632 | " 0.00006 0.00006 0.00004 0.00005 0.00004 0.00014]]\n"
633 | ]
634 | }
635 | ],
636 | "source": [
637 | "#prepare asset parameters for k-means clustering\n",
638 | "#reshape for concatenation\n",
639 | "meanReturns = meanReturns.reshape(len(meanReturns),1)\n",
640 | "assetParameters = np.concatenate([meanReturns, covReturns], axis = 1)\n",
641 | "print('Size of the asset parameters for clustering:\\n', assetParameters.shape)\n",
642 | "print('Asset parameters for clustering:\\n', assetParameters)"
643 | ]
644 | },
645 | {
646 | "cell_type": "markdown",
647 | "metadata": {},
648 | "source": [
649 | "**Step 5:** ***Group the assets into clusters using k-means clustering where k =15, which is the portfolio size selected by the investor.*** \n",
650 | " \n",
651 | "The Python code shows the invocation of the function **KMeans** from the scikit-learn library. \n",
652 | "The centroids (special points in multidimensional space) towards which the the other physical points representing the asset parameters gravitated to, based on their similarity measure and hence formed a cluster with the centroid as its nucleus, has been listed in the output. Observe that 15 centroids are obtained for $k=15$. Each point in the multi-dimensional space including the centroids, are of dimension 30. The labels indicate the cluster to which point $i$ among $N$ points or asset $i$ of the $N$-stock universe in reality, belong to. "
653 | ]
654 | },
655 | {
656 | "cell_type": "code",
657 | "execution_count": 20,
658 | "metadata": {},
659 | "outputs": [
660 | {
661 | "name": "stdout",
662 | "output_type": "stream",
663 | "text": [
664 | "Clustering of assets completed!\n",
665 | "Centroids:\n",
666 | " [[ 0.00029 0.00004 0.00004 0.00005 0.00004 0.00006 0.00004 0.00004\n",
667 | " 0.00004 0.00006 0.00004 0.00005 0.00004 0.00004 0.00003 0.00004\n",
668 | " 0.00004 0.00005 0.00005 0.00005 0.00004 0.00004 0.00004 0.00005\n",
669 | " 0.00004 0.00004 0.00004 0.00006 0.00015 0.00004]\n",
670 | " [ 0.00099 0.0001 0.00008 0.00012 0.0001 0.00009 0.00007 0.00007\n",
671 | " 0.0001 0.00007 0.00007 0.0001 0.00006 0.00009 0.00004 0.00005\n",
672 | " 0.00008 0.00006 0.00013 0.00008 0.00006 0.00004 0.00006 0.0001\n",
673 | " 0.00008 0.00011 0.00004 0.00008 0.00005 0.00006]\n",
674 | " [-0.00011 0.00007 0.00006 0.00008 0.0001 0.00008 0.0001 0.00006\n",
675 | " 0.00008 0.00006 0.00011 0.00008 0.00005 0.00008 0.00004 0.00004\n",
676 | " 0.00007 0.00006 0.00008 0.00005 0.00005 0.00004 0.00005 0.00005\n",
677 | " 0.00006 0.00007 0.00004 0.00005 0.00004 0.0001 ]\n",
678 | " [ 0.00034 0.00005 0.00005 0.00006 0.00006 0.00006 0.00005 0.00004\n",
679 | " 0.00006 0.00005 0.00005 0.00006 0.00007 0.00006 0.00004 0.00004\n",
680 | " 0.00006 0.0001 0.00006 0.00005 0.00009 0.00004 0.00005 0.00006\n",
681 | " 0.00005 0.00006 0.00004 0.00006 0.00004 0.00005]\n",
682 | " [ 0.00082 0.00008 0.00007 0.00008 0.00008 0.00008 0.00006 0.00006\n",
683 | " 0.00008 0.00011 0.00006 0.00007 0.00005 0.00007 0.00004 0.00005\n",
684 | " 0.00006 0.00005 0.00008 0.00015 0.00005 0.00004 0.00005 0.00007\n",
685 | " 0.00006 0.00008 0.00004 0.00007 0.00005 0.00005]\n",
686 | " [ 0.00039 0.0001 0.0001 0.00013 0.00027 0.0001 0.00012 0.00007\n",
687 | " 0.00013 0.00008 0.00009 0.00012 0.00005 0.00012 0.00004 0.00005\n",
688 | " 0.00011 0.00006 0.00011 0.00008 0.00006 0.00004 0.00007 0.00007\n",
689 | " 0.0001 0.0001 0.00004 0.00007 0.00004 0.00011]\n",
690 | " [ 0.00077 0.0001 0.00007 0.00009 0.00011 0.00015 0.00008 0.00007\n",
691 | " 0.0001 0.00007 0.00008 0.00018 0.00006 0.00009 0.00004 0.00005\n",
692 | " 0.00008 0.00006 0.00012 0.00008 0.00006 0.00004 0.00006 0.00007\n",
693 | " 0.00008 0.00009 0.00005 0.00007 0.00005 0.00007]\n",
694 | " [ 0.00017 0.00007 0.00007 0.00009 0.00011 0.00008 0.00013 0.00006\n",
695 | " 0.00009 0.00006 0.00007 0.00008 0.00005 0.00008 0.00004 0.00004\n",
696 | " 0.00007 0.00005 0.00008 0.00006 0.00005 0.00004 0.00006 0.00006\n",
697 | " 0.0001 0.00007 0.00004 0.00006 0.00004 0.00009]\n",
698 | " [ 0.00042 0.00006 0.00006 0.00007 0.00008 0.00007 0.00006 0.00008\n",
699 | " 0.00008 0.00006 0.00006 0.00007 0.00005 0.00008 0.00004 0.00004\n",
700 | " 0.00008 0.00005 0.00007 0.00006 0.00005 0.00004 0.00007 0.00006\n",
701 | " 0.00006 0.00007 0.00004 0.00006 0.00004 0.00006]\n",
702 | " [ 0.00022 0.00003 0.00003 0.00004 0.00004 0.00004 0.00004 0.00004\n",
703 | " 0.00004 0.00004 0.00004 0.00004 0.00004 0.00004 0.00006 0.00004\n",
704 | " 0.00004 0.00004 0.00005 0.00004 0.00004 0.00006 0.00004 0.00003\n",
705 | " 0.00004 0.00004 0.00007 0.00004 0.00004 0.00004]\n",
706 | " [ 0.00057 0.00005 0.00004 0.00005 0.00005 0.00005 0.00004 0.00004\n",
707 | " 0.00005 0.00005 0.00004 0.00004 0.00004 0.00005 0.00004 0.00011\n",
708 | " 0.00004 0.00004 0.00006 0.00005 0.00003 0.00004 0.00004 0.00004\n",
709 | " 0.00004 0.00005 0.00004 0.00004 0.00004 0.00004]\n",
710 | " [-0.00002 0.00007 0.00007 0.00007 0.00007 0.00007 0.00006 0.00006\n",
711 | " 0.00008 0.00007 0.00005 0.00007 0.00006 0.00007 0.00004 0.00004\n",
712 | " 0.00006 0.00006 0.00008 0.00007 0.00006 0.00004 0.00006 0.00008\n",
713 | " 0.00006 0.00007 0.00005 0.00026 0.00006 0.00005]\n",
714 | " [ 0.00031 0.00008 0.00014 0.00009 0.00011 0.00008 0.00008 0.00007\n",
715 | " 0.00016 0.00007 0.00007 0.00009 0.00005 0.00013 0.00003 0.00004\n",
716 | " 0.00007 0.00006 0.00009 0.00007 0.00006 0.00004 0.00007 0.00008\n",
717 | " 0.00008 0.00009 0.00004 0.00007 0.00004 0.00007]\n",
718 | " [ 0.0006 0.00008 0.0001 0.0001 0.00012 0.00009 0.00009 0.00007\n",
719 | " 0.00016 0.00008 0.00008 0.00009 0.00005 0.00017 0.00003 0.00005\n",
720 | " 0.00008 0.00007 0.00009 0.00007 0.00006 0.00004 0.00008 0.00008\n",
721 | " 0.00008 0.00009 0.00004 0.00007 0.00004 0.00008]\n",
722 | " [ 0.0009 0.00024 0.00007 0.0001 0.0001 0.0001 0.00007 0.00007\n",
723 | " 0.0001 0.00007 0.00007 0.00011 0.00005 0.00008 0.00003 0.00005\n",
724 | " 0.00007 0.00005 0.00012 0.00008 0.00005 0.00004 0.00005 0.00008\n",
725 | " 0.00007 0.0001 0.00003 0.00007 0.00004 0.00006]]\n",
726 | "Labels:\n",
727 | " [14 12 1 5 6 7 8 12 4 2 6 3 13 9 10 8 3 1 4 3 9 8 1 7\n",
728 | " 1 9 11 0 2]\n"
729 | ]
730 | }
731 | ],
732 | "source": [
733 | "#kmeans clustering of assets using the characteristic vector of \n",
734 | "#mean return and variance-covariance vector of returns\n",
735 | "\n",
736 | "assetsCluster= KMeans(algorithm='auto', max_iter=600, n_clusters=clusters)\n",
737 | "print('Clustering of assets completed!') \n",
738 | "assetsCluster.fit(assetParameters)\n",
739 | "centroids = assetsCluster.cluster_centers_\n",
740 | "labels = assetsCluster.labels_\n",
741 | "\n",
742 | "print('Centroids:\\n', centroids)\n",
743 | "print('Labels:\\n', labels)"
744 | ]
745 | },
746 | {
747 | "cell_type": "markdown",
748 | "metadata": {},
749 | "source": [
750 | "**Step 6:** ***Fix asset labels to points in each cluster***"
751 | ]
752 | },
753 | {
754 | "cell_type": "code",
755 | "execution_count": 21,
756 | "metadata": {},
757 | "outputs": [
758 | {
759 | "name": "stdout",
760 | "output_type": "stream",
761 | "text": [
762 | "Stocks in each of the clusters:\n",
763 | "\n",
764 | "Cluster 1\n",
765 | "['WMT']\n",
766 | "Cluster 2\n",
767 | "['BA' 'MSFT' 'UNH' 'V']\n",
768 | "Cluster 3\n",
769 | "['IBM' 'XOM']\n",
770 | "Cluster 4\n",
771 | "['JNJ' 'MRK' 'PFE']\n",
772 | "Cluster 5\n",
773 | "['HD' 'NKE']\n",
774 | "Cluster 6\n",
775 | "['CAT']\n",
776 | "Cluster 7\n",
777 | "['CSCO' 'INTC']\n",
778 | "Cluster 8\n",
779 | "['CVX' 'UTX']\n",
780 | "Cluster 9\n",
781 | "['DIS' 'MMM' 'TRV']\n",
782 | "Cluster 10\n",
783 | "['KO' 'PG' 'VZ']\n",
784 | "Cluster 11\n",
785 | "['MCD']\n",
786 | "Cluster 12\n",
787 | "['WBA']\n",
788 | "Cluster 13\n",
789 | "['AXP' 'GS']\n",
790 | "Cluster 14\n",
791 | "['JPM']\n",
792 | "Cluster 15\n",
793 | "['AAPL']\n"
794 | ]
795 | }
796 | ],
797 | "source": [
798 | "#fixing asset labels to cluster points\n",
799 | "print('Stocks in each of the clusters:\\n',)\n",
800 | "assets = np.array(assetLabels)\n",
801 | "for i in range(clusters):\n",
802 | " print('Cluster', i+1)\n",
803 | " clt = np.where(labels == i)\n",
804 | " assetsCluster = assets[clt]\n",
805 | " print(assetsCluster)\n",
806 | " "
807 | ]
808 | },
809 | {
810 | "cell_type": "markdown",
811 | "metadata": {},
812 | "source": [
813 | "It can be seen that the 29 assets of the stock universe have been grouped into 15 clusters. The idea conveyed is that all assets in the same cluster behave **similar** (*inter-cluster similarity*) with regard to their mean and variance-covariance of returns, and are **dissimilar** (*intra-cluster dissimilarity*) with regard to the same characteristics with those assets in other clusters."
814 | ]
815 | },
816 | {
817 | "cell_type": "markdown",
818 | "metadata": {},
819 | "source": [
820 | "Therefore picking **one asset from each cluster** to gather a portfolio of 15 assets would ensure that the portfolio is well-diversified with regard to these characteristics. The choice could be random or preferential, but restricted to one asset from each cluster. For ease of reference, we term the choices made as **$k$-portfolio**. Needless to say, multiple $k$-portfolios can be generated from these clusters. "
821 | ]
822 | },
823 | {
824 | "cell_type": "markdown",
825 | "metadata": {},
826 | "source": [
827 | "Since $k$-means clustering is a heuristic method which produces clusters that are sensitive to the randomly chosen initial centroids, it may yield different cluster configurations with each run. In practice, an aggregation of the clusters yielded during various runs can be studied to make the appropriate choice of assets one each from each cluster to construct the well-diversified portfolio."
828 | ]
829 | },
830 | {
831 | "cell_type": "markdown",
832 | "metadata": {},
833 | "source": [
834 | "## 3.5 $k$-portfolios - a brief note"
835 | ]
836 | },
837 | {
838 | "cell_type": "markdown",
839 | "metadata": {},
840 | "source": [
841 | "Let us suppose that a specific run of the $k$-means clustering algorithm yielded the following clusters for the DJIA index \"mini\" universe:\n",
842 | "[KO PG VZ WMT], [UNH], [DIS MMM TRV], [IBM, XOM], [CSCO INTC], [JPM], [GS], [WBA], [AAPL MSFT V], [HD NKE], [AXP CVX UTX], [MCD],[JNJ MRK PFE], [BA], [CAT] \n",
843 | " \n",
844 | "The investor is now free to make a choice of one asset each from each of the clusters. The choice could be random or guided by individual preferences. The following $k$-portfolios are some sample choices that can be made by investors. \n",
845 | "\n",
846 | "**$k$-portfolio 1**: \n",
847 | "{ [KO], [UNH], [DIS], [IBM], [CSCO], [JPM], [GS], [WBA], [AAPL], [HD], [AXP], [MCD], [MRK], [BA], [CAT] } \n",
848 | "\n",
849 | "**$k$-portfolio 2**: \n",
850 | "{ [VZ], [UNH], [MMM], [XOM], [INTC], [JPM], [GS], [WBA], [MSFT], [NKE], [CVX], [MCD], [PFE], [BA], [CAT]} \n",
851 | "\n",
852 | "**$k$-portfolio 3**: \n",
853 | "{ [WMT], [UNH], [TRV], [IBM], [CSCO], [JPM], [GS], [WBA], [V], [NKE], [UTX], [MCD], [JNJ], [BA], [CAT]} \n",
854 | "\n",
855 | "An investor can opt to invest in any one of the $k$-portfolios. Fig. 3.5 illustrates heuristic selection of portfolios where the portfolio of assets is diversified in behaviour adhering to the adage \" never put all eggs in the basket\". "
856 | ]
857 | },
858 | {
859 | "attachments": {},
860 | "cell_type": "markdown",
861 | "metadata": {},
862 | "source": [
863 | "\n",
864 | "#### Fig. 3.5 Heuristic selection of assets to construct a diversified portfolio "
865 | ]
866 | },
867 | {
868 | "cell_type": "markdown",
869 | "metadata": {},
870 | "source": [
871 | "Indeed, several questions do arise on the behavior of the $k$-portfolios, their risk-return tradeoffs and their performance when time tested portfolio construction techniques are applied over them. These shall be covered in detail in the ensuing lessons. "
872 | ]
873 | },
874 | {
875 | "cell_type": "markdown",
876 | "metadata": {},
877 | "source": [
878 | "### Companion Reading \n",
879 | "\n",
880 | "\n"
881 | ]
882 | },
883 | {
884 | "cell_type": "markdown",
885 | "metadata": {},
886 | "source": [
887 | "This work is an abridged adaptation of concepts discussed in Chapter 3 of [PAI 18] to Dow Jones dataset (DJIA Index: April, 2014-April, 2019) and implemented in Python using NumPy, Pandas and scikit-learn libraries. Readers (read \"worker bees\") seeking more information may refer to the corresponding chapter in the book."
888 | ]
889 | },
890 | {
891 | "cell_type": "markdown",
892 | "metadata": {},
893 | "source": [
894 | "References
\n",
895 | "\n",
896 | "[CHO 08] Choueifaty Yves and Y Coignard, Toward Maximum Diversification, *The Journal of Portfolio Management*, pp. 40-51, 2008.\n",
897 | "\n",
898 | "[CHO 13] Choueifaty Yves, T Froidure and J Reynier, Properties of the Most Diversified Portfolio, *Journal of Investment Strategies*, 2(2), pp. 49-70, 2013. \n",
899 | " \n",
900 | "[PAI 18] Vijayalakshmi Pai G. A., Metaheuristics for Portfolio Optimization- *An Introduction using MATLAB*, Wiley-ISTE, 2018. https://www.mathworks.com/academia/books/metaheuristics-for-portfolio-optimization-pai.html \n",
901 | "\n",
902 | "\n"
903 | ]
904 | },
905 | {
906 | "cell_type": "markdown",
907 | "metadata": {},
908 | "source": [
909 | "#### Next.....Lesson 4: Traditional Methods for Portfolio Construction"
910 | ]
911 | },
912 | {
913 | "cell_type": "markdown",
914 | "metadata": {},
915 | "source": [
916 | ""
917 | ]
918 | },
919 | {
920 | "cell_type": "code",
921 | "execution_count": null,
922 | "metadata": {},
923 | "outputs": [],
924 | "source": []
925 | }
926 | ],
927 | "metadata": {
928 | "kernelspec": {
929 | "display_name": "Python 3",
930 | "language": "python",
931 | "name": "python3"
932 | },
933 | "language_info": {
934 | "codemirror_mode": {
935 | "name": "ipython",
936 | "version": 3
937 | },
938 | "file_extension": ".py",
939 | "mimetype": "text/x-python",
940 | "name": "python",
941 | "nbconvert_exporter": "python",
942 | "pygments_lexer": "ipython3",
943 | "version": "3.7.3"
944 | }
945 | },
946 | "nbformat": 4,
947 | "nbformat_minor": 2
948 | }
949 |
--------------------------------------------------------------------------------
/Lesson4_TraditionalPortfolioConstruction/Lesson4Eqn4_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaiViji/PythonFinance-PortfolioOptimization/2bb0ae9f32f44ff66302ea33f53c02e746583a05/Lesson4_TraditionalPortfolioConstruction/Lesson4Eqn4_1.png
--------------------------------------------------------------------------------
/Lesson4_TraditionalPortfolioConstruction/Lesson4Eqn4_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaiViji/PythonFinance-PortfolioOptimization/2bb0ae9f32f44ff66302ea33f53c02e746583a05/Lesson4_TraditionalPortfolioConstruction/Lesson4Eqn4_2.png
--------------------------------------------------------------------------------
/Lesson4_TraditionalPortfolioConstruction/Lesson4ExitTailImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaiViji/PythonFinance-PortfolioOptimization/2bb0ae9f32f44ff66302ea33f53c02e746583a05/Lesson4_TraditionalPortfolioConstruction/Lesson4ExitTailImage.png
--------------------------------------------------------------------------------
/Lesson4_TraditionalPortfolioConstruction/Lesson4GoalHeaderImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaiViji/PythonFinance-PortfolioOptimization/2bb0ae9f32f44ff66302ea33f53c02e746583a05/Lesson4_TraditionalPortfolioConstruction/Lesson4GoalHeaderImage.png
--------------------------------------------------------------------------------
/Lesson4_TraditionalPortfolioConstruction/Lesson4_MainContent.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "### Lesson 4\n"
8 | ]
9 | },
10 | {
11 | "cell_type": "markdown",
12 | "metadata": {},
13 | "source": [
14 | "# Traditional Methods for Portfolio Construction"
15 | ]
16 | },
17 | {
18 | "cell_type": "markdown",
19 | "metadata": {},
20 | "source": [
21 | "\n"
22 | ]
23 | },
24 | {
25 | "cell_type": "markdown",
26 | "metadata": {},
27 | "source": [
28 | "## 4.1 Introduction"
29 | ]
30 | },
31 | {
32 | "cell_type": "markdown",
33 | "metadata": {},
34 | "source": [
35 | "An investor essentially has to do a lot of study and research before the assets comprising the portfolio are selected. **Lesson 3 Heuristic Portfolio Selection** detailed on how $k$-means clustering could be employed to undertake a heuristic selection of assets that ensures diversification. Termed **$k$-portfolios**, these portfolios ensured diversification by virtue of the choices made, one each from each cluster generated by the clustering method, which are inherently dissimilar in behaviour. Nevertheless, several questions need to be answered with regard to the behaviour of $k$-portfolios. "
36 | ]
37 | },
38 | {
39 | "cell_type": "markdown",
40 | "metadata": {},
41 | "source": [
42 | "The first and foremost question is *what is the diversification index of a $k$-portfolio*? In other words, having decided to invest in a $k$-portfolio, how would an investor know how diversified is the portfolio? \n",
43 | " \n",
44 | "In this regard, one among several diversification indices available in the finance literature, viz., **Diversification Ratio** was introduced in Lesson 3. However, to compute the diversification index, the investor should have decided on the apportionment of capital over each of the assets in the portfolio. In other words, the asset allocation **weights** (introduced in **Lesson 1 Fundamentals of Risk and Return of a Portfolio**) must have been determined. "
45 | ]
46 | },
47 | {
48 | "cell_type": "markdown",
49 | "metadata": {},
50 | "source": [
51 | "Needless to say, determining the asset allocation weights is an equally, if not more, complex task especially when investors are strongly inclined to follow some popular investment strategies or express preference for certain assets or asset classes in the portfolio or are bound by company norms or religious laws etc. We shall defer discussion on these complex asset allocation models and for now, focus on traditional and time tested methods of determining asset allocation weights. "
52 | ]
53 | },
54 | {
55 | "cell_type": "markdown",
56 | "metadata": {},
57 | "source": [
58 | "Two traditional methods of asset allocation, which are considered as benchmark portfolio construction strategies are introduced in this lesson. They are, \n",
59 | "\n",
60 | "(1) **Equal weighted portfolio construction**, and \n",
61 | "(2) **Inverse volatility weighted portfolio construction**. \n",
62 | "\n",
63 | "The two techniques shall be applied over $k$-portfolios to determine their Diversification Ratios and study their behaviour. "
64 | ]
65 | },
66 | {
67 | "cell_type": "markdown",
68 | "metadata": {},
69 | "source": [
70 | "## 4.2 Equal Weighted Portfolio Construction"
71 | ]
72 | },
73 | {
74 | "cell_type": "markdown",
75 | "metadata": {},
76 | "source": [
77 | "An **equal weighted portfolio construction** treats all stocks in the portfolio as equal and allots equal weights. Thus the weight vector of a portfolio comprising $N$ stocks is given by \n",
78 | " "
79 | ]
80 | },
81 | {
82 | "cell_type": "markdown",
83 | "metadata": {},
84 | "source": [
85 | "\n",
86 | "..........(4.1)
"
87 | ]
88 | },
89 | {
90 | "cell_type": "markdown",
91 | "metadata": {},
92 | "source": [
93 | "The following Python code applies equal weighted portfolio construction method for the DJIA $k$-portfolio (**$k$-portfolio 1**) discussed in Sec. 3.5 of **Lesson 3 Heuristic Portfolio Selection**. The $k$-portfolio comprises the following stocks: \n",
94 | "\n",
95 | "𝑘-portfolio 1:\n",
96 | "\n",
97 | "{Coca-Cola (KO), United Health (UNH), Walt Disney (DIS), IBM (IBM), Cisco (CSCO), JPMorgan Chase (JPM), Goldman Sachs (GS), Walgreens Boots Alliance (WBA), Apple (AAPL), Home Depot (HD), American Express (AXP), McDonald's (MCD), Merck (MRK), Boeing (BA), Caterpillar (CAT)} "
98 | ]
99 | },
100 | {
101 | "cell_type": "code",
102 | "execution_count": 10,
103 | "metadata": {},
104 | "outputs": [
105 | {
106 | "name": "stdout",
107 | "output_type": "stream",
108 | "text": [
109 | "k-portfolio 1 asset labels:\n",
110 | " ['AAPL', 'AXP', 'BA', 'CAT', 'CSCO', 'DIS', 'GS', 'HD', 'IBM', 'JPM', 'KO', 'MCD', 'MRK', 'UNH', 'WBA']\n",
111 | "k-portfolio 1 dataset size:\n",
112 | " 1258 15\n",
113 | "k-portfolio 1 stock prices:\n",
114 | " [[ 74.52571 85.5 123.25 ... 55.57 79.18 65.67 ]\n",
115 | " [ 73.99429 86.04 124.27 ... 56.05 79.51 66.01 ]\n",
116 | " [ 74.14429 87.4 126.04 ... 56.26 78.19 66.16 ]\n",
117 | " ...\n",
118 | " [199.5 109.85 369.04001 ... 80.8 248.78999 54.5 ]\n",
119 | " [200.61999 110.16 364.94 ... 80.82 246.03 54.51 ]\n",
120 | " [198.95 109.85 370.16 ... 79.84 235.42 53.44 ]]\n"
121 | ]
122 | }
123 | ],
124 | "source": [
125 | "#read k portfolio 1 dataset comprising 15 stocks\n",
126 | "\n",
127 | "#dependencies\n",
128 | "import numpy as np\n",
129 | "import pandas as pd\n",
130 | "\n",
131 | "#input stock dataset\n",
132 | "StockFileName = 'DJIA_Apr112014_Apr112019_kpf1.csv'\n",
133 | "Rows = 1259 #excluding header\n",
134 | "Columns = 15 #excluding date\n",
135 | "\n",
136 | "#read stock prices \n",
137 | "df = pd.read_csv(StockFileName, nrows= Rows)\n",
138 | "\n",
139 | "#extract asset labels\n",
140 | "assetLabels = df.columns[1:Columns+1].tolist()\n",
141 | "print('k-portfolio 1 asset labels:\\n', assetLabels)\n",
142 | "\n",
143 | "#extract stock prices excluding header and trading dates\n",
144 | "dfStockPrices = df.iloc[1:, 1:]\n",
145 | "\n",
146 | "#store stock prices as an array\n",
147 | "arStockPrices = np.asarray(dfStockPrices)\n",
148 | "[rows, cols]= arStockPrices.shape\n",
149 | "print('k-portfolio 1 dataset size:\\n', rows, cols)\n",
150 | "print('k-portfolio 1 stock prices:\\n', arStockPrices)"
151 | ]
152 | },
153 | {
154 | "cell_type": "markdown",
155 | "metadata": {},
156 | "source": [
157 | "The $k$-portfolio returns are computed as discussed in **Lesson 1 Fundamentals of risk and return of a portfolio**. The Python function to compute daily returns and the code fragment that runs it over DJIA $k$-portfolio 1 are shown below."
158 | ]
159 | },
160 | {
161 | "cell_type": "code",
162 | "execution_count": 11,
163 | "metadata": {},
164 | "outputs": [],
165 | "source": [
166 | "#function to compute stock returns\n",
167 | "def StockReturnsComputing(StockPrice, Rows, Columns):\n",
168 | " \n",
169 | " import numpy as np\n",
170 | " \n",
171 | " StockReturn = np.zeros([Rows-1, Columns])\n",
172 | " for j in range(Columns): # j: Assets\n",
173 | " for i in range(Rows-1): #i: Daily Prices\n",
174 | " StockReturn[i,j]=((StockPrice[i+1, j]-StockPrice[i,j])/StockPrice[i,j])\n",
175 | "\n",
176 | " return StockReturn"
177 | ]
178 | },
179 | {
180 | "cell_type": "code",
181 | "execution_count": 12,
182 | "metadata": {},
183 | "outputs": [
184 | {
185 | "name": "stdout",
186 | "output_type": "stream",
187 | "text": [
188 | "k-portfolio 1 returns:\n",
189 | " [[-0.00713 0.00632 0.00828 ... 0.00864 0.00417 0.00518]\n",
190 | " [ 0.00203 0.01581 0.01424 ... 0.00375 -0.0166 0.00227]\n",
191 | " [ 0.01143 -0.0135 0.01492 ... 0.00373 -0.03082 0.00892]\n",
192 | " ...\n",
193 | " [-0.003 -0.00768 -0.01463 ... -0.00185 0.00016 -0.01017]\n",
194 | " [ 0.00561 0.00282 -0.01111 ... 0.00025 -0.01109 0.00018]\n",
195 | " [-0.00832 -0.00281 0.0143 ... -0.01213 -0.04312 -0.01963]]\n"
196 | ]
197 | }
198 | ],
199 | "source": [
200 | "#compute asset returns\n",
201 | "arReturns = StockReturnsComputing(arStockPrices, rows, cols)\n",
202 | "print('k-portfolio 1 returns:\\n', arReturns)"
203 | ]
204 | },
205 | {
206 | "cell_type": "markdown",
207 | "metadata": {},
208 | "source": [
209 | "The mean returns and the variance-covariance matrix of returns are computed as demonstrated in the Python code shown below."
210 | ]
211 | },
212 | {
213 | "cell_type": "code",
214 | "execution_count": 13,
215 | "metadata": {},
216 | "outputs": [
217 | {
218 | "name": "stdout",
219 | "output_type": "stream",
220 | "text": [
221 | "Mean returns of k-portfolio 1:\n",
222 | " [ 0.0009 0.00028 0.00099 0.00038 0.0008 0.00039 0.00032 0.00085\n",
223 | " -0.00017 0.00061 0.00019 0.00056 0.00036 0.00095 -0.00003]\n",
224 | "\n",
225 | "Variance-Covariance matrix of returns of k-portfolio 1: \n",
226 | "\n",
227 | "Size (15, 15) \n",
228 | " [[0.00024 0.00007 0.0001 0.0001 0.0001 0.00007 0.0001 0.00007 0.00007\n",
229 | " 0.00008 0.00003 0.00005 0.00005 0.00008 0.00007]\n",
230 | " [0.00007 0.00016 0.00008 0.00009 0.00007 0.00006 0.00011 0.00007 0.00007\n",
231 | " 0.0001 0.00003 0.00003 0.00006 0.00007 0.00007]\n",
232 | " [0.0001 0.00008 0.00023 0.00013 0.00009 0.00007 0.00011 0.00007 0.00008\n",
233 | " 0.0001 0.00004 0.00005 0.00006 0.00007 0.00007]\n",
234 | " [0.0001 0.00009 0.00013 0.00027 0.0001 0.00007 0.00013 0.00008 0.00009\n",
235 | " 0.00012 0.00004 0.00005 0.00006 0.00007 0.00007]\n",
236 | " [0.0001 0.00007 0.00009 0.0001 0.00018 0.00007 0.00009 0.00007 0.00008\n",
237 | " 0.00009 0.00004 0.00005 0.00006 0.00007 0.00007]\n",
238 | " [0.00007 0.00006 0.00007 0.00007 0.00007 0.00014 0.00008 0.00006 0.00006\n",
239 | " 0.00007 0.00003 0.00004 0.00005 0.00006 0.00006]\n",
240 | " [0.0001 0.00011 0.00011 0.00013 0.00009 0.00008 0.00021 0.00008 0.00008\n",
241 | " 0.00016 0.00003 0.00005 0.00007 0.00008 0.00008]\n",
242 | " [0.00007 0.00007 0.00007 0.00008 0.00007 0.00006 0.00008 0.00014 0.00006\n",
243 | " 0.00008 0.00003 0.00005 0.00005 0.00007 0.00007]\n",
244 | " [0.00007 0.00007 0.00008 0.00009 0.00008 0.00006 0.00008 0.00006 0.00016\n",
245 | " 0.00008 0.00004 0.00004 0.00006 0.00006 0.00005]\n",
246 | " [0.00008 0.0001 0.0001 0.00012 0.00009 0.00007 0.00016 0.00008 0.00008\n",
247 | " 0.00017 0.00003 0.00005 0.00007 0.00008 0.00007]\n",
248 | " [0.00003 0.00003 0.00004 0.00004 0.00004 0.00003 0.00003 0.00003 0.00004\n",
249 | " 0.00003 0.00008 0.00004 0.00004 0.00003 0.00004]\n",
250 | " [0.00005 0.00003 0.00005 0.00005 0.00005 0.00004 0.00005 0.00005 0.00004\n",
251 | " 0.00005 0.00004 0.00011 0.00004 0.00004 0.00004]\n",
252 | " [0.00005 0.00006 0.00006 0.00006 0.00006 0.00005 0.00007 0.00005 0.00006\n",
253 | " 0.00007 0.00004 0.00004 0.00015 0.00006 0.00006]\n",
254 | " [0.00008 0.00007 0.00007 0.00007 0.00007 0.00006 0.00008 0.00007 0.00006\n",
255 | " 0.00008 0.00003 0.00004 0.00006 0.00017 0.00008]\n",
256 | " [0.00007 0.00007 0.00007 0.00007 0.00007 0.00006 0.00008 0.00007 0.00005\n",
257 | " 0.00007 0.00004 0.00004 0.00006 0.00008 0.00026]]\n"
258 | ]
259 | }
260 | ],
261 | "source": [
262 | "#compute mean returns and variance covariance matrix of returns\n",
263 | "\n",
264 | "#set precision for printing results\n",
265 | "np.set_printoptions(precision=5, suppress = True)\n",
266 | "\n",
267 | "meanReturns = np.mean(arReturns, axis = 0)\n",
268 | "print('Mean returns of k-portfolio 1:\\n', meanReturns)\n",
269 | "covReturns = np.cov(arReturns, rowvar=False)\n",
270 | "print('\\nVariance-Covariance matrix of returns of k-portfolio 1: \\n')\n",
271 | "print('Size ', covReturns.shape, '\\n', covReturns)"
272 | ]
273 | },
274 | {
275 | "cell_type": "markdown",
276 | "metadata": {},
277 | "source": [
278 | "Applying equal weighted portfolio construction method to $k$-portfolio 1 and obtaining the annualized risk (%) and the expected annual return (%) of the portfolio (as discussed in **Lesson 1 Fundamentals of risk and return of a portfolio**) yields the following."
279 | ]
280 | },
281 | {
282 | "cell_type": "code",
283 | "execution_count": 14,
284 | "metadata": {},
285 | "outputs": [
286 | {
287 | "name": "stdout",
288 | "output_type": "stream",
289 | "text": [
290 | "Annualized Portfolio Risk : 13.68 %\n",
291 | "\n",
292 | "Annualized Expected Portfolio Return: 12.34 %\n"
293 | ]
294 | }
295 | ],
296 | "source": [
297 | "#equal weighted portfolio construction: Annualized risk and \n",
298 | "#expected annualized portfolio return\n",
299 | "#trading days = 251\n",
300 | "PortfolioSize = Columns\n",
301 | "EqualWeightVector = np.ones((1,PortfolioSize))*(1.0/PortfolioSize)\n",
302 | "EqWgtPortfolioRisk = np.sqrt(np.matmul((np.matmul(EqualWeightVector,covReturns)), \\\n",
303 | " np.transpose(EqualWeightVector)))\n",
304 | "EqWgtAnnPortfolioRisk = EqWgtPortfolioRisk*np.sqrt(251)*100\n",
305 | "EqWgtPortfolioReturn = np.matmul(EqualWeightVector, np.transpose(meanReturns))\n",
306 | "EqWgtAnnPortfolioReturn = 251*EqWgtPortfolioReturn * 100\n",
307 | "\n",
308 | "print(\"Annualized Portfolio Risk : %4.2f\" % EqWgtAnnPortfolioRisk, \"%\")\n",
309 | "print(\"\\nAnnualized Expected Portfolio Return: %4.2f\" % EqWgtAnnPortfolioReturn,\"%\")\n"
310 | ]
311 | },
312 | {
313 | "cell_type": "markdown",
314 | "metadata": {},
315 | "source": [
316 | "### Diversification Ratio of the Equal Weighted $k$-Portfolio"
317 | ]
318 | },
319 | {
320 | "cell_type": "markdown",
321 | "metadata": {},
322 | "source": [
323 | "Following equation (3.1) in **Lesson 3 Heuristic Portfolio Selection**, the Diversification Ratio of $k$-portfolio 1 is computed as follows. "
324 | ]
325 | },
326 | {
327 | "cell_type": "code",
328 | "execution_count": 15,
329 | "metadata": {},
330 | "outputs": [
331 | {
332 | "name": "stdout",
333 | "output_type": "stream",
334 | "text": [
335 | "\n",
336 | " Equal Weighted Portfolio:Diversification Ratio 1.53\n"
337 | ]
338 | }
339 | ],
340 | "source": [
341 | "# Equal weighted portfolio: Diversification Ratio\n",
342 | "EqWgtPortfolioAssetStdDev = np.sqrt(np.diagonal(covReturns))\n",
343 | "EqWgtPortfolioDivRatio = np.sum(np.multiply(EqWgtPortfolioAssetStdDev, EqualWeightVector)) \\\n",
344 | " / EqWgtPortfolioRisk\n",
345 | "print(\"\\n Equal Weighted Portfolio:Diversification Ratio %4.2f\" % EqWgtPortfolioDivRatio)\n"
346 | ]
347 | },
348 | {
349 | "cell_type": "markdown",
350 | "metadata": {},
351 | "source": [
352 | "## 4.3 Inverse Volatility Weighted Portfolio"
353 | ]
354 | },
355 | {
356 | "cell_type": "markdown",
357 | "metadata": {},
358 | "source": [
359 | "An **inverse volatility weighted portfolio** is one in which highly volatile assets are allotted smaller weights and low volatile assets are allotted larger weights. Thus, the weights of the individual assets in the portfolio are proportional to the reciprocals of their individual volatilities. \n",
360 | "The weights are given by, \n"
361 | ]
362 | },
363 | {
364 | "cell_type": "markdown",
365 | "metadata": {},
366 | "source": [
367 | "\n",
368 | "..........(4.2)
\n"
369 | ]
370 | },
371 | {
372 | "cell_type": "markdown",
373 | "metadata": {},
374 | "source": [
375 | "The following Python code applies inverse volatility weighted portfolio construction method for the DJIA $k$-portfolio (**$k$-portfolio 1**) discussed in Sec. 4.2. \n",
376 | "\n",
377 | "Since the diagonal of the variance-covariance matrix of returns **covReturns** denotes the variance of returns, the individual asset risk $\\sigma_i$ of asset $i$ in the $k$-portfolio, which is defined to be the standard deviation of asset returns, can be computed as square root of the variance. **InvVolWeightAssets_Risk** in the Python code shown below does just that. \n"
378 | ]
379 | },
380 | {
381 | "cell_type": "markdown",
382 | "metadata": {},
383 | "source": [
384 | "The annualized risk (%) and the expected annual return (%) of the inverse volatility weighted $k$-portfolio have been shown in the output."
385 | ]
386 | },
387 | {
388 | "cell_type": "code",
389 | "execution_count": 16,
390 | "metadata": {},
391 | "outputs": [
392 | {
393 | "name": "stdout",
394 | "output_type": "stream",
395 | "text": [
396 | "Annualized Portfolio Risk: 13.24 %\n",
397 | "\n",
398 | "Annualized Expected Portfolio Return: 12.13 %\n"
399 | ]
400 | }
401 | ],
402 | "source": [
403 | "#Inverse volatility weighted portfolio construction: Annualized risk and \n",
404 | "#Expected annualized portfolio return\n",
405 | "#Trading days = 251\n",
406 | "InvVolWeightAssets_Risk = np.sqrt(np.diagonal(covReturns))\n",
407 | "InvVolWeightAssets_ReciprocalRisk = 1.0/InvVolWeightAssets_Risk\n",
408 | "InvVolWeightAssets_ReciprocalRisk_Sum = np.sum(InvVolWeightAssets_ReciprocalRisk)\n",
409 | "InvVolWeightAssets_Weights = InvVolWeightAssets_ReciprocalRisk / \\\n",
410 | " InvVolWeightAssets_ReciprocalRisk_Sum\n",
411 | "InvVolWeightPortfolio_Risk = np.sqrt(np.matmul((np.matmul(InvVolWeightAssets_Weights,\\\n",
412 | " covReturns)), np.transpose(InvVolWeightAssets_Weights)))\n",
413 | "\n",
414 | "#annualized risk and return\n",
415 | "InvVolWeightPortfolio_AnnRisk = np.sqrt(251)* InvVolWeightPortfolio_Risk *100\n",
416 | "InvVolWeightPortfolio_AnnReturn = 251* np.matmul(InvVolWeightAssets_Weights,\\\n",
417 | " np.transpose(meanReturns)) *100\n",
418 | "\n",
419 | "print(\"Annualized Portfolio Risk: %4.2f\" % InvVolWeightPortfolio_AnnRisk,\"%\\n\")\n",
420 | "print(\"Annualized Expected Portfolio Return: %4.2f\" % InvVolWeightPortfolio_AnnReturn,\"%\")\n"
421 | ]
422 | },
423 | {
424 | "cell_type": "markdown",
425 | "metadata": {},
426 | "source": [
427 | "The Diversification Ratio of the Inverse Volatility Weighted Portfolio is shown in the output. "
428 | ]
429 | },
430 | {
431 | "cell_type": "code",
432 | "execution_count": 20,
433 | "metadata": {},
434 | "outputs": [
435 | {
436 | "name": "stdout",
437 | "output_type": "stream",
438 | "text": [
439 | "\n",
440 | " Inverse Volatility Weighted Portfolio:Diversification Ratio 1.54\n"
441 | ]
442 | }
443 | ],
444 | "source": [
445 | "# Inverse volatility weighted portfolio: Diversification Ratio\n",
446 | "InvVolWeightAssets_Risk= np.sqrt(np.diagonal(covReturns))\n",
447 | "InvVolWeightPortfolioDivRatio = \\\n",
448 | "np.sum(np.multiply(InvVolWeightAssets_Risk, InvVolWeightAssets_Weights))\\\n",
449 | "/ InvVolWeightPortfolio_Risk\n",
450 | "print(\"\\n Inverse Volatility Weighted Portfolio:Diversification Ratio %4.2f\" \\\n",
451 | " % InvVolWeightPortfolioDivRatio)\n"
452 | ]
453 | },
454 | {
455 | "cell_type": "markdown",
456 | "metadata": {},
457 | "source": [
458 | "## 4.4 \"Ideal\" Equal Weighted and Inverse Volatility Weighted Portfolios"
459 | ]
460 | },
461 | {
462 | "cell_type": "markdown",
463 | "metadata": {},
464 | "source": [
465 | "The investor chose to invest in a $k$-portfolio of 15 stocks selected out of the \"mini-universe\" of 29 DJIA stocks. The equal weighted portfolio construction yielded a Diversification Ratio of 1.53 and the inverse volatility weighted portfolio construction yielded a Diversification Ratio of 1.54. The annualized risk and expected portfolio return of the equal weighted and inverse volatility weighted portfolios were (13.68%, 12.34%) and (13.24%, 12.13%) respectively. \n",
466 | "Now, was investing on the $k$-portfolio worth the effort? What if the investor had chosen to invest in the whole \"mini-universe\", which needless to say is an \"ideal\" portfolio? Would the behaviour of the \"ideal\" portfolio surpass that of the contending portfolios? \n",
467 | "\n",
468 | "Repeating the portfolio construction methods over the \"mini-universe\" and running the Python code all over again with the CSV file whose details have been provided in the Python code shown below, yields an output, snapshots of which are shown in the succeeding cells."
469 | ]
470 | },
471 | {
472 | "cell_type": "code",
473 | "execution_count": 18,
474 | "metadata": {},
475 | "outputs": [],
476 | "source": [
477 | "#identify the \"mini\" stock universe dataset\n",
478 | "StockFileName = 'DJIA_Apr112014_Apr112019.csv'\n",
479 | "Rows = 1259 #excluding header\n",
480 | "Columns = 29 #excluding date"
481 | ]
482 | },
483 | {
484 | "cell_type": "markdown",
485 | "metadata": {},
486 | "source": [
487 | "The \"ideal\" equal weighted porfolio yields the following results:"
488 | ]
489 | },
490 | {
491 | "cell_type": "markdown",
492 | "metadata": {},
493 | "source": [
494 | "**\"Ideal\" Equal Weighted Portfolio** \n",
495 | "\n",
496 | "Annualized portfolio risk: 12.84% \n",
497 | "Expected Annualized portfolio return: 11.75% \n",
498 | "Diversification Ratio: 1.57"
499 | ]
500 | },
501 | {
502 | "cell_type": "markdown",
503 | "metadata": {},
504 | "source": [
505 | "The \"ideal\" inverse volatility weighted portfolio yields the following results:"
506 | ]
507 | },
508 | {
509 | "cell_type": "markdown",
510 | "metadata": {},
511 | "source": [
512 | "**\"Ideal\" Inverse Volatility Weighted Portfolio** \n",
513 | "\n",
514 | "Annualized portfolio risk: 12.47% \n",
515 | "Expected Annualized portfolio return: 11.29% \n",
516 | "Diversification Ratio: 1.57\n"
517 | ]
518 | },
519 | {
520 | "cell_type": "markdown",
521 | "metadata": {},
522 | "source": [
523 | "A comparison of the \"ideal\" portfolio behaviour with that of the $k$-portfolio's behaviour yields the following observations: \n",
524 | "\n",
525 | "(1) Even if an investor had enough capital to invest in all the stocks in the \"mini-universe\", despite employing time-tested portfolio construction methods such as equal weighted or inverse volatility weighted construction, the behaviour of the \"ideal\" portfolio does not surpass that of its contenders and hence is not a remarkable choice to opt for. \n",
526 | "\n",
527 | "(2) The Diversification Ratios of both equal weighted and inverse volatility weighted $k$-portfolios are in proximity to that of the \"ideal\" portfolio, conveying a significant message that prudent selection of stocks ($k$-means clustering in this case) can ensure a diversification index as much as that of an \"ideal\" portfolio that invests in the complete universe of stocks. \n",
528 | "\n",
529 | "(3) Diversification does not mean investing in a large number of stocks, but making a prudent choice of stocks, smaller in number yet diverse from one another in behaviour. A $k$-portfolio guarantees diversification in this sense. \n",
530 | "\n",
531 | "(4) A portfolio that holds a large number of stocks, for the sake of diversification, can give rise to huge transaction costs and management fees. $k$-portfolios in this regard are economical with regard to these costs. \n",
532 | "\n",
533 | "(5) $k$-portfolios by and large assure **risk-parity investing**, where higher returns are ensured for an equal amount of risk and diversification ratio, as that of an \"ideal\" portfolio.\n"
534 | ]
535 | },
536 | {
537 | "cell_type": "markdown",
538 | "metadata": {},
539 | "source": [
540 | "### Companion Reading \n"
541 | ]
542 | },
543 | {
544 | "cell_type": "markdown",
545 | "metadata": {},
546 | "source": [
547 | "\n",
548 | "This blog is an abridged adaptation of concepts discussed in Chapter 3 of [PAI 18] to Dow Jones dataset (DJIA index: April, 2014- April, 2019) and implemented in Python. Readers (read \"worker bees\"), seeking more information may refer to the corresponding chapter in the book.\n"
549 | ]
550 | },
551 | {
552 | "cell_type": "markdown",
553 | "metadata": {},
554 | "source": [
555 | "References
\n",
556 | "\n",
557 | "[PAI 18] Vijayalakshmi Pai G. A., Metaheuristics for Portfolio Optimization- *An \n",
558 | " Introduction using MATLAB*, Wiley-ISTE, 2018. https://www.mathworks.com/academia/books/metaheuristics-for-portfolio-optimization-pai.html "
559 | ]
560 | },
561 | {
562 | "cell_type": "markdown",
563 | "metadata": {},
564 | "source": [
565 | "#### Next.....Lesson 5: Mean-Variance Optimization of Portfolios"
566 | ]
567 | },
568 | {
569 | "cell_type": "markdown",
570 | "metadata": {},
571 | "source": [
572 | ""
573 | ]
574 | },
575 | {
576 | "cell_type": "code",
577 | "execution_count": null,
578 | "metadata": {},
579 | "outputs": [],
580 | "source": []
581 | }
582 | ],
583 | "metadata": {
584 | "kernelspec": {
585 | "display_name": "Python 3",
586 | "language": "python",
587 | "name": "python3"
588 | },
589 | "language_info": {
590 | "codemirror_mode": {
591 | "name": "ipython",
592 | "version": 3
593 | },
594 | "file_extension": ".py",
595 | "mimetype": "text/x-python",
596 | "name": "python",
597 | "nbconvert_exporter": "python",
598 | "pygments_lexer": "ipython3",
599 | "version": "3.7.3"
600 | }
601 | },
602 | "nbformat": 4,
603 | "nbformat_minor": 2
604 | }
605 |
--------------------------------------------------------------------------------
/Lesson5_MeanVarianceOptimization/DJIAkpfsIdeal_EfficientFrontiersComparison.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaiViji/PythonFinance-PortfolioOptimization/2bb0ae9f32f44ff66302ea33f53c02e746583a05/Lesson5_MeanVarianceOptimization/DJIAkpfsIdeal_EfficientFrontiersComparison.png
--------------------------------------------------------------------------------
/Lesson5_MeanVarianceOptimization/Lesson5Eqn5_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaiViji/PythonFinance-PortfolioOptimization/2bb0ae9f32f44ff66302ea33f53c02e746583a05/Lesson5_MeanVarianceOptimization/Lesson5Eqn5_1.png
--------------------------------------------------------------------------------
/Lesson5_MeanVarianceOptimization/Lesson5Eqn5_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaiViji/PythonFinance-PortfolioOptimization/2bb0ae9f32f44ff66302ea33f53c02e746583a05/Lesson5_MeanVarianceOptimization/Lesson5Eqn5_2.png
--------------------------------------------------------------------------------
/Lesson5_MeanVarianceOptimization/Lesson5Eqn5_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaiViji/PythonFinance-PortfolioOptimization/2bb0ae9f32f44ff66302ea33f53c02e746583a05/Lesson5_MeanVarianceOptimization/Lesson5Eqn5_3.png
--------------------------------------------------------------------------------
/Lesson5_MeanVarianceOptimization/Lesson5Eqn5_4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaiViji/PythonFinance-PortfolioOptimization/2bb0ae9f32f44ff66302ea33f53c02e746583a05/Lesson5_MeanVarianceOptimization/Lesson5Eqn5_4.png
--------------------------------------------------------------------------------
/Lesson5_MeanVarianceOptimization/Lesson5Eqn5_5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaiViji/PythonFinance-PortfolioOptimization/2bb0ae9f32f44ff66302ea33f53c02e746583a05/Lesson5_MeanVarianceOptimization/Lesson5Eqn5_5.png
--------------------------------------------------------------------------------
/Lesson5_MeanVarianceOptimization/Lesson5Eqn5_6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaiViji/PythonFinance-PortfolioOptimization/2bb0ae9f32f44ff66302ea33f53c02e746583a05/Lesson5_MeanVarianceOptimization/Lesson5Eqn5_6.png
--------------------------------------------------------------------------------
/Lesson5_MeanVarianceOptimization/Lesson5ExitTailImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaiViji/PythonFinance-PortfolioOptimization/2bb0ae9f32f44ff66302ea33f53c02e746583a05/Lesson5_MeanVarianceOptimization/Lesson5ExitTailImage.png
--------------------------------------------------------------------------------
/Lesson5_MeanVarianceOptimization/Lesson5Fig5_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaiViji/PythonFinance-PortfolioOptimization/2bb0ae9f32f44ff66302ea33f53c02e746583a05/Lesson5_MeanVarianceOptimization/Lesson5Fig5_1.png
--------------------------------------------------------------------------------
/Lesson5_MeanVarianceOptimization/Lesson5Fig5_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaiViji/PythonFinance-PortfolioOptimization/2bb0ae9f32f44ff66302ea33f53c02e746583a05/Lesson5_MeanVarianceOptimization/Lesson5Fig5_2.png
--------------------------------------------------------------------------------
/Lesson5_MeanVarianceOptimization/Lesson5GoalHeaderImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaiViji/PythonFinance-PortfolioOptimization/2bb0ae9f32f44ff66302ea33f53c02e746583a05/Lesson5_MeanVarianceOptimization/Lesson5GoalHeaderImage.png
--------------------------------------------------------------------------------
/Lesson6_SharpeRatioOptimization/Lesson6Eqn6_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaiViji/PythonFinance-PortfolioOptimization/2bb0ae9f32f44ff66302ea33f53c02e746583a05/Lesson6_SharpeRatioOptimization/Lesson6Eqn6_1.png
--------------------------------------------------------------------------------
/Lesson6_SharpeRatioOptimization/Lesson6Eqn6_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaiViji/PythonFinance-PortfolioOptimization/2bb0ae9f32f44ff66302ea33f53c02e746583a05/Lesson6_SharpeRatioOptimization/Lesson6Eqn6_2.png
--------------------------------------------------------------------------------
/Lesson6_SharpeRatioOptimization/Lesson6Eqn6_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaiViji/PythonFinance-PortfolioOptimization/2bb0ae9f32f44ff66302ea33f53c02e746583a05/Lesson6_SharpeRatioOptimization/Lesson6Eqn6_3.png
--------------------------------------------------------------------------------
/Lesson6_SharpeRatioOptimization/Lesson6Eqn6_4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaiViji/PythonFinance-PortfolioOptimization/2bb0ae9f32f44ff66302ea33f53c02e746583a05/Lesson6_SharpeRatioOptimization/Lesson6Eqn6_4.png
--------------------------------------------------------------------------------
/Lesson6_SharpeRatioOptimization/Lesson6ExitTailImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaiViji/PythonFinance-PortfolioOptimization/2bb0ae9f32f44ff66302ea33f53c02e746583a05/Lesson6_SharpeRatioOptimization/Lesson6ExitTailImage.png
--------------------------------------------------------------------------------
/Lesson6_SharpeRatioOptimization/Lesson6GoalHeaderImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaiViji/PythonFinance-PortfolioOptimization/2bb0ae9f32f44ff66302ea33f53c02e746583a05/Lesson6_SharpeRatioOptimization/Lesson6GoalHeaderImage.png
--------------------------------------------------------------------------------
/Lesson6_SharpeRatioOptimization/Lesson6_MainContent.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "### Lesson 6\n"
8 | ]
9 | },
10 | {
11 | "cell_type": "markdown",
12 | "metadata": {},
13 | "source": [
14 | "# Sharpe Ratio based Portfolio Optimization "
15 | ]
16 | },
17 | {
18 | "attachments": {},
19 | "cell_type": "markdown",
20 | "metadata": {},
21 | "source": [
22 | ""
23 | ]
24 | },
25 | {
26 | "cell_type": "markdown",
27 | "metadata": {},
28 | "source": [
29 | "## 6.1 Introduction"
30 | ]
31 | },
32 | {
33 | "cell_type": "markdown",
34 | "metadata": {},
35 | "source": [
36 | "**Sharpe Ratio**, developed by Nobel Laureate William F Sharpe [SHA 66], is a measure of calculating risk adjusted return. It serves to help investors know about the returns on their investments relative to the risks they hold. The Sharpe Ratio is defined as"
37 | ]
38 | },
39 | {
40 | "cell_type": "markdown",
41 | "metadata": {},
42 | "source": [
43 | "\n",
44 | "..........(6.1)
\n"
45 | ]
46 | },
47 | {
48 | "cell_type": "markdown",
49 | "metadata": {},
50 | "source": [
51 | "where $r_x$ is the average rate of return on the investment $x$, $R_f$, the best available **risk free rate of return** and $\\sigma$ the standard deviation of $r_x$, which denotes the risk on the investment. "
52 | ]
53 | },
54 | {
55 | "cell_type": "markdown",
56 | "metadata": {},
57 | "source": [
58 | "Higher the Sharpe Ratio, more is the excess returns over that of holding a risk free investment, relative to the increased volatility that the investment is exposed to. A Sharpe Ratio of 0, needless to say, only denotes the investment to be risk-free or one that does not yield any excess return. In practice, while a Sharpe ratio of 1 marks the investment to be acceptable or good for investors, a value less than 1 grades the investment as sub-optimal, and values greater than 1 and moving towards 2 or 3, grades the investment as highly superior. "
59 | ]
60 | },
61 | {
62 | "cell_type": "markdown",
63 | "metadata": {},
64 | "source": [
65 | "## 6.2 Maximizing Sharpe Ratio"
66 | ]
67 | },
68 | {
69 | "cell_type": "markdown",
70 | "metadata": {},
71 | "source": [
72 | "Having understood the significance of the Sharpe Ratio, let us suppose an investor wishes to make an investment in assets in such a way that the Sharpe Ratio of the portfolio would be the best possible or the maximum, that can be ensured for the investment. \n"
73 | ]
74 | },
75 | {
76 | "cell_type": "markdown",
77 | "metadata": {},
78 | "source": [
79 | "Let P be a portfolio comprising assets $A_1, A_2, ...A_N$, with $\\mu_1, \\mu_2, ...\\mu_N$ as the asset returns and $W_1, W_2, ...W_N$ as the weights. \n",
80 | " "
81 | ]
82 | },
83 | {
84 | "cell_type": "markdown",
85 | "metadata": {},
86 | "source": [
87 | "The portfolio return $r$ determined by a weighted summation of its individual asset returns is given by, $\\sum\\left({W_i.\\mu_i}\\right)$ and the risk is given by $\\sqrt{\\sum\\sum {W_i.W_j.\\sigma_{ij}} } $. (See **Lesson 1 Fundamentals of Risk and Return of a Portfolio** to know about risk and return of a portfolio). "
88 | ]
89 | },
90 | {
91 | "cell_type": "markdown",
92 | "metadata": {},
93 | "source": [
94 | "To keep the discussion simple for now, let us suppose that the investor decides to enforce only **basic constraints** on the portfolio. (See Sec. 5.2 of **Lesson 5 Mean Variance Optimization of Portfolios** to know about basic constraints). \n",
95 | " \n",
96 | "The mathematical model for the Sharpe Ratio based Portfolio Optimization is given by, "
97 | ]
98 | },
99 | {
100 | "cell_type": "markdown",
101 | "metadata": {},
102 | "source": [
103 | "\n",
104 | "..........(6.2)
"
105 | ]
106 | },
107 | {
108 | "cell_type": "markdown",
109 | "metadata": {},
110 | "source": [
111 | "The numerator of the objective function denotes the excess returns of the investment over that of a risk free asset $R_f$ and the denominator the risk of the investment. The objective is to maximize the Sharpe Ratio. The basic constraints indicate that the investor wishes to have a fully invested portfolio. "
112 | ]
113 | },
114 | {
115 | "cell_type": "markdown",
116 | "metadata": {},
117 | "source": [
118 | "## 6.3 Solving the Sharpe Ratio Optimization Model"
119 | ]
120 | },
121 | {
122 | "cell_type": "markdown",
123 | "metadata": {},
124 | "source": [
125 | "To solve the Sharpe Ratio maximization model represented by (6.2), we make use of the **minimize** library function from **scipy.optimize** package of Python. However, since the original objective function insists on maximization as opposed to minimization demanded by the **minimize** solver, the **principal of duality** borrowed from Optimization Theory is employed to undertake the transformation. According to the principle, \n"
126 | ]
127 | },
128 | {
129 | "cell_type": "markdown",
130 | "metadata": {},
131 | "source": [
132 | "\n",
133 | "..........(6.3)
"
134 | ]
135 | },
136 | {
137 | "cell_type": "markdown",
138 | "metadata": {},
139 | "source": [
140 | "The Python code for the function **MaximizeSharpeRatioOptimization** which defines the objective function and the basic constraints represented by (6.2), is shown below: "
141 | ]
142 | },
143 | {
144 | "cell_type": "code",
145 | "execution_count": 21,
146 | "metadata": {},
147 | "outputs": [],
148 | "source": [
149 | "#function to undertake Sharpe Ratio maximization subject to \n",
150 | "#basic constraints of the portfolio\n",
151 | "\n",
152 | "#dependencies\n",
153 | "import numpy as np\n",
154 | "from scipy import optimize \n",
155 | "\n",
156 | "def MaximizeSharpeRatioOptmzn(MeanReturns, CovarReturns, RiskFreeRate, PortfolioSize):\n",
157 | " \n",
158 | " # define maximization of Sharpe Ratio using principle of duality\n",
159 | " def f(x, MeanReturns, CovarReturns, RiskFreeRate, PortfolioSize):\n",
160 | " funcDenomr = np.sqrt(np.matmul(np.matmul(x, CovarReturns), x.T) )\n",
161 | " funcNumer = np.matmul(np.array(MeanReturns),x.T)-RiskFreeRate\n",
162 | " func = -(funcNumer / funcDenomr)\n",
163 | " return func\n",
164 | "\n",
165 | " #define equality constraint representing fully invested portfolio\n",
166 | " def constraintEq(x):\n",
167 | " A=np.ones(x.shape)\n",
168 | " b=1\n",
169 | " constraintVal = np.matmul(A,x.T)-b \n",
170 | " return constraintVal\n",
171 | " \n",
172 | " \n",
173 | " #define bounds and other parameters\n",
174 | " xinit=np.repeat(0.33, PortfolioSize)\n",
175 | " cons = ({'type': 'eq', 'fun':constraintEq})\n",
176 | " lb = 0\n",
177 | " ub = 1\n",
178 | " bnds = tuple([(lb,ub) for x in xinit])\n",
179 | " \n",
180 | " #invoke minimize solver\n",
181 | " opt = optimize.minimize (f, x0 = xinit, args = (MeanReturns, CovarReturns,\\\n",
182 | " RiskFreeRate, PortfolioSize), method = 'SLSQP', \\\n",
183 | " bounds = bnds, constraints = cons, tol = 10**-3)\n",
184 | " \n",
185 | " return opt\n",
186 | " "
187 | ]
188 | },
189 | {
190 | "cell_type": "markdown",
191 | "metadata": {},
192 | "source": [
193 | "The Sharpe Ratio optimization requires the computation of risk and return of the portfolio. The asset returns computing function **StockReturnsComputing**, is reproduced here for the reader's convenience. "
194 | ]
195 | },
196 | {
197 | "cell_type": "code",
198 | "execution_count": 22,
199 | "metadata": {},
200 | "outputs": [],
201 | "source": [
202 | "# function computes asset returns \n",
203 | "def StockReturnsComputing(StockPrice, Rows, Columns):\n",
204 | " \n",
205 | " import numpy as np\n",
206 | " \n",
207 | " StockReturn = np.zeros([Rows-1, Columns])\n",
208 | " for j in range(Columns): # j: Assets\n",
209 | " for i in range(Rows-1): # i: Daily Prices\n",
210 | " StockReturn[i,j]=((StockPrice[i+1, j]-StockPrice[i,j])/StockPrice[i,j])* 100\n",
211 | "\n",
212 | " return StockReturn"
213 | ]
214 | },
215 | {
216 | "cell_type": "markdown",
217 | "metadata": {},
218 | "source": [
219 | "## 6.4 Case Study"
220 | ]
221 | },
222 | {
223 | "cell_type": "markdown",
224 | "metadata": {},
225 | "source": [
226 | " Let us suppose that an investor decides to invest in a $k$-portfolio ($k$-portfolio 1) comprising the following Dow stocks. ($k$ portfolio 1, is detailed and listed in **Lesson 3 Heuristic Portfolio Selection**) \n",
227 | "\n",
228 | "**𝑘-portfolio 1**: \n",
229 | "\n",
230 | "{Coca-Cola (KO), United Health (UNH), Walt Disney (DIS), IBM (IBM), Cisco (CSCO), JPMorgan Chase (JPM), Goldman Sachs (GS), Walgreens Boots Alliance (WBA), Apple (AAPL), Home Depot (HD), American Express (AXP), McDonald's (MCD), Merck (MRK), Boeing (BA), Caterpillar (CAT)} \n",
231 | "\n",
232 | "The investor desires to explore the optimal portfolio set that would yield the maximal Sharpe Ratio. The objective is to find out the optimal weights that will ensure maximal Sharpe Ratio for the portfolio.\n",
233 | "\n",
234 | "The following Python code reads the dataset concerned, computes the stock returns using the Python function **StockReturnsComputing** and obtains the mean returns and the variance-covariance matrix of returns. \n"
235 | ]
236 | },
237 | {
238 | "cell_type": "code",
239 | "execution_count": 23,
240 | "metadata": {},
241 | "outputs": [
242 | {
243 | "name": "stdout",
244 | "output_type": "stream",
245 | "text": [
246 | "Asset labels of k-portfolio 1: \n",
247 | " ['AAPL', 'AXP', 'BA', 'CAT', 'CSCO', 'DIS', 'GS', 'HD', 'IBM', 'JPM', 'KO', 'MCD', 'MRK', 'UNH', 'WBA']\n",
248 | "\n",
249 | "Mean Returns:\n",
250 | " [ 0.09 0.029 0.1 0.039 0.081 0.04 0.033 0.085 -0.016 0.06\n",
251 | " 0.019 0.057 0.036 0.095 -0.002]\n",
252 | "\n",
253 | "Variance-Covariance Matrix of Returns:\n",
254 | " [[2.375 0.672 0.962 1.042 0.999 0.68 0.954 0.726 0.709 0.825 0.306 0.458\n",
255 | " 0.534 0.774 0.697]\n",
256 | " [0.672 1.648 0.8 0.95 0.7 0.569 1.065 0.658 0.663 1.001 0.307 0.35\n",
257 | " 0.556 0.718 0.667]\n",
258 | " [0.962 0.8 2.288 1.31 0.89 0.716 1.066 0.747 0.777 0.977 0.381 0.472\n",
259 | " 0.578 0.745 0.679]\n",
260 | " [1.042 0.95 1.31 2.733 1.041 0.688 1.321 0.796 0.885 1.169 0.358 0.455\n",
261 | " 0.616 0.72 0.681]\n",
262 | " [0.999 0.7 0.89 1.041 1.789 0.713 0.927 0.724 0.817 0.909 0.362 0.477\n",
263 | " 0.647 0.656 0.707]\n",
264 | " [0.68 0.569 0.716 0.688 0.713 1.35 0.773 0.586 0.574 0.717 0.302 0.368\n",
265 | " 0.466 0.557 0.631]\n",
266 | " [0.954 1.065 1.066 1.321 0.927 0.773 2.114 0.795 0.803 1.554 0.303 0.467\n",
267 | " 0.705 0.82 0.819]\n",
268 | " [0.726 0.658 0.747 0.796 0.724 0.586 0.795 1.39 0.619 0.753 0.343 0.472\n",
269 | " 0.487 0.659 0.689]\n",
270 | " [0.709 0.663 0.777 0.885 0.817 0.574 0.803 0.619 1.632 0.767 0.372 0.391\n",
271 | " 0.576 0.564 0.534]\n",
272 | " [0.825 1.001 0.977 1.169 0.909 0.717 1.554 0.753 0.767 1.702 0.324 0.483\n",
273 | " 0.675 0.761 0.717]\n",
274 | " [0.306 0.307 0.381 0.358 0.362 0.302 0.303 0.343 0.372 0.324 0.806 0.36\n",
275 | " 0.384 0.31 0.355]\n",
276 | " [0.458 0.35 0.472 0.455 0.477 0.368 0.467 0.472 0.391 0.483 0.36 1.086\n",
277 | " 0.402 0.43 0.433]\n",
278 | " [0.534 0.556 0.578 0.616 0.647 0.466 0.705 0.487 0.576 0.675 0.384 0.402\n",
279 | " 1.504 0.615 0.64 ]\n",
280 | " [0.774 0.718 0.745 0.72 0.656 0.557 0.82 0.659 0.564 0.761 0.31 0.43\n",
281 | " 0.615 1.722 0.78 ]\n",
282 | " [0.697 0.667 0.679 0.681 0.707 0.631 0.819 0.689 0.534 0.717 0.355 0.433\n",
283 | " 0.64 0.78 2.554]]\n"
284 | ]
285 | }
286 | ],
287 | "source": [
288 | "#obtain mean and variance-covariance matrix of returns for k-portfolio 1\n",
289 | "\n",
290 | "#Dependencies\n",
291 | "import numpy as np\n",
292 | "import pandas as pd\n",
293 | "\n",
294 | "\n",
295 | "\n",
296 | "#input k portfolio 1 dataset comprising 15 stocks\n",
297 | "StockFileName = 'DJIA_Apr112014_Apr112019_kpf1.csv'\n",
298 | "Rows = 1259 #excluding header\n",
299 | "Columns = 15 #excluding date\n",
300 | "\n",
301 | "#read stock prices \n",
302 | "df = pd.read_csv(StockFileName, nrows= Rows)\n",
303 | "\n",
304 | "#extract asset labels\n",
305 | "assetLabels = df.columns[1:Columns+1].tolist()\n",
306 | "print('Asset labels of k-portfolio 1: \\n', assetLabels)\n",
307 | "\n",
308 | "#read asset prices data\n",
309 | "StockData = df.iloc[0:, 1:]\n",
310 | "\n",
311 | "#compute asset returns\n",
312 | "arStockPrices = np.asarray(StockData)\n",
313 | "[Rows, Cols]=arStockPrices.shape\n",
314 | "arReturns = StockReturnsComputing(arStockPrices, Rows, Cols)\n",
315 | "\n",
316 | "#set precision for printing results\n",
317 | "np.set_printoptions(precision=3, suppress = True)\n",
318 | "\n",
319 | "#compute mean returns and variance covariance matrix of returns\n",
320 | "meanReturns = np.mean(arReturns, axis = 0)\n",
321 | "covReturns = np.cov(arReturns, rowvar=False)\n",
322 | "print('\\nMean Returns:\\n', meanReturns)\n",
323 | "print('\\nVariance-Covariance Matrix of Returns:\\n', covReturns)\n"
324 | ]
325 | },
326 | {
327 | "cell_type": "markdown",
328 | "metadata": {},
329 | "source": [
330 | "The annual average risk free rate of return in USA during April 2019 was 3%. The daily risk free rate is computed as \n"
331 | ]
332 | },
333 | {
334 | "cell_type": "markdown",
335 | "metadata": {},
336 | "source": [
337 | "\n",
338 | "..........(6.4)
\n"
339 | ]
340 | },
341 | {
342 | "cell_type": "markdown",
343 | "metadata": {},
344 | "source": [
345 | "The following Python code computes the maximal Sharpe Ratio for $k$-portfolio 1."
346 | ]
347 | },
348 | {
349 | "cell_type": "code",
350 | "execution_count": 24,
351 | "metadata": {},
352 | "outputs": [
353 | {
354 | "name": "stdout",
355 | "output_type": "stream",
356 | "text": [
357 | "\n",
358 | "Risk free rate (daily %): 0.008\n",
359 | "Maximal Sharpe Ratio: [[1.26]] \n",
360 | "Annualized Risk (%): [[14.749]] \n",
361 | "Annualized Expected Portfolio Return(%): [21.584]\n",
362 | "\n",
363 | "Optimal weights (%):\n",
364 | " [[13.694]\n",
365 | " [ 0. ]\n",
366 | " [17.744]\n",
367 | " [ 0. ]\n",
368 | " [12.151]\n",
369 | " [ 0. ]\n",
370 | " [ 0. ]\n",
371 | " [19.058]\n",
372 | " [ 0. ]\n",
373 | " [ 1.151]\n",
374 | " [ 0. ]\n",
375 | " [13.654]\n",
376 | " [ 0. ]\n",
377 | " [22.547]\n",
378 | " [ 0. ]]\n"
379 | ]
380 | }
381 | ],
382 | "source": [
383 | "#obtain maximal Sharpe Ratio for k-portfolio 1 of Dow stocks\n",
384 | "\n",
385 | "#set portfolio size\n",
386 | "portfolioSize = Columns\n",
387 | "\n",
388 | "#set risk free asset rate of return\n",
389 | "Rf=3 # April 2019 average risk free rate of return in USA approx 3%\n",
390 | "annRiskFreeRate = Rf/100\n",
391 | "\n",
392 | "#compute daily risk free rate in percentage\n",
393 | "r0 = (np.power((1 + annRiskFreeRate), (1.0 / 360.0)) - 1.0) * 100 \n",
394 | "print('\\nRisk free rate (daily %): ', end=\"\")\n",
395 | "print (\"{0:.3f}\".format(r0)) \n",
396 | "\n",
397 | "#initialization\n",
398 | "xOptimal =[]\n",
399 | "minRiskPoint = []\n",
400 | "expPortfolioReturnPoint =[]\n",
401 | "maxSharpeRatio = 0\n",
402 | "\n",
403 | "#compute maximal Sharpe Ratio and optimal weights\n",
404 | "result = MaximizeSharpeRatioOptmzn(meanReturns, covReturns, r0, portfolioSize)\n",
405 | "xOptimal.append(result.x)\n",
406 | "\n",
407 | " \n",
408 | "#compute risk returns and max Sharpe Ratio of the optimal portfolio \n",
409 | "xOptimalArray = np.array(xOptimal)\n",
410 | "Risk = np.matmul((np.matmul(xOptimalArray,covReturns)), np.transpose(xOptimalArray))\n",
411 | "expReturn = np.matmul(np.array(meanReturns),xOptimalArray.T)\n",
412 | "annRisk = np.sqrt(Risk*251) \n",
413 | "annRet = 251*np.array(expReturn) \n",
414 | "maxSharpeRatio = (annRet-Rf)/annRisk \n",
415 | "\n",
416 | "#set precision for printing results\n",
417 | "np.set_printoptions(precision=3, suppress = True)\n",
418 | "\n",
419 | "\n",
420 | "#display results\n",
421 | "print('Maximal Sharpe Ratio: ', maxSharpeRatio, '\\nAnnualized Risk (%): ', \\\n",
422 | " annRisk, '\\nAnnualized Expected Portfolio Return(%): ', annRet)\n",
423 | "print('\\nOptimal weights (%):\\n', xOptimalArray.T*100 )"
424 | ]
425 | },
426 | {
427 | "cell_type": "markdown",
428 | "metadata": {},
429 | "source": [
430 | "The output shows that the maximal Sharpe Ratio attainable for $k$-portfolio 1 is 1.26 which is good, going by practical standards. The annual expected portfolio return is 21.584% against an annualized risk of 14.749%. To achieve this, the optimal capital allocations on the assets of $k$-portfolio 1 are as follows: \n",
431 | "\n",
432 | "['AAPL': 13. 694%] , ['BA': 17.744%], ['CSCO': 12.151%], ['HD': 19.058%], ['JPM': 1.151%], ['MCD': 13.654%], ['UNH': 22.547%]. \n",
433 | " \n",
434 | "No investments need be made in the rest of the assets of $k$-portfolio 1 since the optimal weights arrived at for these assets are 0. \n",
435 | "However, if the investor desires to invest in all the assets in the $k$-portfolio 1, with the weights distributed across all the assets in the portfolio, all that the investor needs to do is to redefine the bounds constraint of (6.2) as, $0\\lt W_i\\lt 1$ and run the **scipy** solver over the optimization model."
436 | ]
437 | },
438 | {
439 | "cell_type": "markdown",
440 | "metadata": {},
441 | "source": [
442 | "Despite the wide use of Sharpe Ratios to compute risk adjusted returns, it is not without its disadvantages. Thus, if the expected returns do not follow a normal distribution or if the portfolios possess non-linear risks, to list a few of the instances, Sharpe Ratios may not deliver results. Therefore, alternative methodologies such as **Sortino Ratio** and **Treynor Ratio** have emerged as contenders to Sharpe Ratios. "
443 | ]
444 | },
445 | {
446 | "cell_type": "markdown",
447 | "metadata": {},
448 | "source": [
449 | "### Companion Reading "
450 | ]
451 | },
452 | {
453 | "cell_type": "markdown",
454 | "metadata": {},
455 | "source": [
456 | "This blog is an abridged adaptation of concepts discussed in Chapter 1 and Chapter 3 of [PAI 18] to Dow Jones dataset (DJIA index: April, 2014- April, 2019) and implemented in Python. Readers (read \"worker bees\"), seeking more information may refer to the corresponding chapters in the book."
457 | ]
458 | },
459 | {
460 | "cell_type": "markdown",
461 | "metadata": {},
462 | "source": [
463 | "References
\n",
464 | " \n",
465 | "[SHA 66] Sharpe, William F. Mutual Fund Performance, *Journal of Business*, January 1966, pp. 119-138. \n",
466 | "\n",
467 | "[PAI 18] Vijayalakshmi Pai G. A., Metaheuristics for Portfolio Optimization- An Introduction using MATLAB, Wiley-ISTE, 2018. https://www.mathworks.com/academia/books/metaheuristics-for-portfolio-optimization-pai.html \n"
468 | ]
469 | },
470 | {
471 | "cell_type": "markdown",
472 | "metadata": {},
473 | "source": [
474 | "#### Next.....Lesson 7: Constrained Portfolio Optimization"
475 | ]
476 | },
477 | {
478 | "cell_type": "markdown",
479 | "metadata": {},
480 | "source": [
481 | ""
482 | ]
483 | },
484 | {
485 | "cell_type": "code",
486 | "execution_count": null,
487 | "metadata": {},
488 | "outputs": [],
489 | "source": []
490 | }
491 | ],
492 | "metadata": {
493 | "kernelspec": {
494 | "display_name": "Python 3",
495 | "language": "python",
496 | "name": "python3"
497 | },
498 | "language_info": {
499 | "codemirror_mode": {
500 | "name": "ipython",
501 | "version": 3
502 | },
503 | "file_extension": ".py",
504 | "mimetype": "text/x-python",
505 | "name": "python",
506 | "nbconvert_exporter": "python",
507 | "pygments_lexer": "ipython3",
508 | "version": "3.7.3"
509 | }
510 | },
511 | "nbformat": 4,
512 | "nbformat_minor": 2
513 | }
514 |
--------------------------------------------------------------------------------
/Lesson7_ConstrainedPortfolioOptimization/Lesson7Eqn7_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaiViji/PythonFinance-PortfolioOptimization/2bb0ae9f32f44ff66302ea33f53c02e746583a05/Lesson7_ConstrainedPortfolioOptimization/Lesson7Eqn7_1.png
--------------------------------------------------------------------------------
/Lesson7_ConstrainedPortfolioOptimization/Lesson7Eqn7_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaiViji/PythonFinance-PortfolioOptimization/2bb0ae9f32f44ff66302ea33f53c02e746583a05/Lesson7_ConstrainedPortfolioOptimization/Lesson7Eqn7_2.png
--------------------------------------------------------------------------------
/Lesson7_ConstrainedPortfolioOptimization/Lesson7Eqn7_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaiViji/PythonFinance-PortfolioOptimization/2bb0ae9f32f44ff66302ea33f53c02e746583a05/Lesson7_ConstrainedPortfolioOptimization/Lesson7Eqn7_3.png
--------------------------------------------------------------------------------
/Lesson7_ConstrainedPortfolioOptimization/Lesson7ExitTailImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaiViji/PythonFinance-PortfolioOptimization/2bb0ae9f32f44ff66302ea33f53c02e746583a05/Lesson7_ConstrainedPortfolioOptimization/Lesson7ExitTailImage.png
--------------------------------------------------------------------------------
/Lesson7_ConstrainedPortfolioOptimization/Lesson7GoalHeaderImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaiViji/PythonFinance-PortfolioOptimization/2bb0ae9f32f44ff66302ea33f53c02e746583a05/Lesson7_ConstrainedPortfolioOptimization/Lesson7GoalHeaderImage.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Repository: PythonFinance-PortfolioOptimization
2 |
3 | Python for Portfolio Optimization: The Ascent!
4 | ==============================================
5 | Python for Portfolio Optimization: The Ascent! First working lessons to ascend the hilly terrain of Portfolio Optimization in seven strides (Lessons), beginning with the fundamentals (Lesson 1) and climbing slope after slope (Lessons 2-6), to reach the first peak of constrained portfolio optimization models (Lesson 7), amongst a range of peaks waiting beyond!
6 |
7 | ----------------------------------------------
8 | Lesson1: Fundamentals of Risk and Return of a Portfolio
9 | (Goal: How does one invest in a portfolio of stocks and know about the returns and risks involved?)
10 | Jupyter Notebook: Lesson1_MainContent.ipynb
11 |
12 | Lesson2: Some glimpses of Financial Data Wrangling
13 | (Goal: Why is it essential to clean and transform raw financial data before they are used to make investment decisions?)
14 | Jupyter Notebook: Lesson2_MainContent.ipynb
15 |
16 | Lesson3: Heuristic Portfolio Selection
17 | (Goal: Given the vast and variegated universe of securities, how can one make a prudent and efficient choice of securities for one's portfolio?)
18 | Jupyter Notebook: Lesson3_MainContent.ipynb
19 |
20 | Lesson 4: Traditional Methods for Portfolio Construction
21 | (Goal: How would an investor know how much to invest in each one of the assets in the portfolio?)
22 | Jupyter Notebook: Lesson4_MainContent.ipynb
23 |
24 | Lesson 5: Mean-Variance Optimization of Portfolios
25 | (Goal: How would one determine the optimal weights which will ensure maximum return and minimum risk for the portfolio that one is invested in?)
26 | Jupyter Notebook: Lesson5_MainContent.ipynb
27 |
28 | Lesson 6: Sharpe Ratio based Portfolio Optimization
29 | (Goal: If a portfolio with higher Sharpe Ratio than its counterparts, is considered superior to them, then how does one invest in the assets of the portfolio, to ensure maximal Sharpe Ratio?)
30 | Jupyter Notebook: Lesson6_MainContent.ipynb
31 |
32 | Lesson 7: Constrained Portfolio Optimization
33 | (Goal: How can an investor know how much to invest in a portfolio of the investor's choice, which besides the objectives of maximizing return and minimizing risk, is constrained by the investor's preference for certain asset classes or assets, or imposition of capital budgets over selective assets in the portfolio?)
34 | Jupyter Notebook: Lesson7_MainContent.ipynb
35 |
36 |
37 |
--------------------------------------------------------------------------------