automating markowitz

21
Jour nal of I nstru ctional Techniques in F in ance Volu me 4, Number 1, 2012 9  In the interest of reducing grading workload and managing cross-student sharing (cheating), we provide an example of automating the construction of Markowitz (1959) efficient frontiers. The program automates the entire procedure from downloading data for a specified number of securities (using either randomly generated securities from a provided list or  specified tickers) to generating graphs of the efficient frontier. Many commercially available optimization programs are available, but this program is free to use and works within Microsoft Excel (2007-2010), the software package most  professors use when creating opt imization assignments. The program i s written in Visual Basic for Applications ( VBA) but does not require any knowledge of VBA to use. The program allows easy creation of random portfolios for student assignments so that cross-student sharing can be minimized without adding an additional grading burden on the professor or TA. The professor can also be assured that the stocks selected produce the desired type of optimization (actually work) before using each set of securities. Where students are allowed to select their own portfolios, the program allows professors to easily check computations and optimizations without having to manually optimize each portfolio. The program can also be used to demonstrate the similarity of efficient frontiers across different stocks, portfolio sizes, and dates. INTRODUCTION Cutbacks in government funding to public universities has resulted in reductions across many areas including but certainly not limited to the hiring of new faculty to keep class sizes small. As class sizes increase, grading of assignments becomes more problematic both in the amount of time required and in the possibility of cross-student sharing (also referred to as free-riding or cheating). In the case of investments courses, many professors require students to complete some type of optimization assignment. With class sizes exceeding 100 students, however, offering individual students the opportunity to select their own stocks makes the grading process difficult or prohibitive. As a result, students are commonly placed in groups, and entire classes may be assigned the same set of stocks to optimize. Although this technique significantly reduces the length of time needed to evaluate the assignments, it also increases the probability of cross-student or cross-group sharing making it difficult to determine which students actually understand the optimization process. One solution to these issues is to use an automated system designed to generate Markowitz (1959) optimizations so that every group or student could be either assigned a separate set of stocks to optimize, or be allowed to select their own securities. In this  paper we describe a VBA program that can be used to automate the optimization of portfolios using Solver in Microsoft Excel. The object is not to teach VBA but to offer some typical code that can be used to expedite a professor’s use of this type of automation. DESCRIPTION OF THE "FRONT-END" OF THE PROGRAM The program is designed for a personal computer (PC) running a recent version of Excel (2007 or 2010) with the Solver add-in installed and does not require any knowledge of VBA, unless the individual professor wishes to adjust the code for his/her own purposes. The program will not run on an Apple operating system but could work if the MAC was dual booted into Windows and was running the appropriate Excel version. Since the VBA code is essentially a large macro, when the file containing the program is opened, two warnings may occur: (1) This file originated from an Internet location and might be unsafe and, (2) Some active content has been disabled. Click for more details. In both cases, clicking “Enable Content” allows the VBA to run. The file will then default to the "Explanation" sheet that contains a brief explanation of the program. As noted in the red box, the program downloads monthly data from Yahoo for either a randomly selected  portfolio of securities or a specific set of ticker symbols. If the user wants random portfolios, the number of securities is entered in cell F1. If the user wants a specific set of securities, the tickers should be entered in column A, starting with cell A1, and cell F1 should be set to 0. Cell F1 is checked first, so if it contains a number between 2 and 20, a random portfolio will be generated even if there are specific tickers in column A. The restriction of 2 to 20 tickers is usually sufficient for most assignments but can easily be altered by changing the VBA code. The program will compute and graph the Markowitz optimization assuming no short selling. Stocks with less than 30 months of data are not used since the mean return is used as the expected return and 30 observations is typically assumed to  be a “reasonable” statistical sample. The user can also change the minimum observation assumption by altering the VBA code. Automating Markowitz Optimizations Using VBA Davi d Port e r and Robe r t Str e tcher  

Upload: ta9

Post on 02-Jun-2018

230 views

Category:

Documents


1 download

TRANSCRIPT

Page 1: Automating Markowitz

8/10/2019 Automating Markowitz

http://slidepdf.com/reader/full/automating-markowitz 1/21

Page 2: Automating Markowitz

8/10/2019 Automating Markowitz

http://slidepdf.com/reader/full/automating-markowitz 2/21

Journal of I nstru ctional Techniques in F in ance Volu me 4, Number 1, 2012

10

Figure 1. Front End Explanation Box

Before clicking “Run Program”, the user should notethe status bar at the bottom of the spreadsheet:

Figure 2. Status Bar.

Figure 3. Prices Sheet.

The status bar reports where the program is at any pointin time. If it appears that nothing is happening, the usershould check the status bar. Also note that the programgenerates six additional spreadsheets. Prices, Returns,BasicStats, Model, ChartData and Graph. The seventh

additional sheet is the list of stocks used for randomlygenerating portfolios. This sheet can be populated by theuser but the original file contains about 500 stocks asexamples. The "Prices" sheet contains the adjusted closing

prices downloaded from Yahoo. Since the program usesmonthly prices, these prices are the last adjusted closing

price of each month.The "Returns" sheet contains the returns computations

based on the downloaded prices. Where the number of prices differs across the securities, the returns are truncatedto the shortest set of data. This is necessary for thecomputation of correlations and covariances. If the number

of returns is less than 30 for any of the securities the sampleis considered too small and the program will terminate.The Basic Stats sheet contains summary measures for

the stocks: means, standard deviations, minimum return,maximum return, and range of returns, as well as thecorrelation matrix.

Figure 4. Returns Sheet.

Page 3: Automating Markowitz

8/10/2019 Automating Markowitz

http://slidepdf.com/reader/full/automating-markowitz 3/21

Journal of I nstru ctional Techniques in F in ance Volu me 4, Number 1, 2012

11

Figure 5. Basic Stats Sheet.

The "Model" sheet contains the data needed for theoptimization: the weights for each stock for the lastoptimization run, the expected returns (the means), the

variance-covariance matrix, the expected return, varianceand standard deviation for the optimized portfolio and thetarget cell. The target cell changes as the program generatesthe chart data from the minimum variance portfolio to themaximum return portfolio.

Figure 6. Model Sheet .

The "ChartData" sheet contains the Solver optimizationoutput for each of the 16 runs from the minimum variance

portfolio to the maximum return – the weights for eachsecurity for each run, as well as the portfolio expected returnand standard deviation. The number of runs is arbitrary and16 (15 Solver runs plus the maximum return portfolio) waschosen as a number that would produce a nice looking graphthe majority of the time.The "Graph" sheet contains a scatter plot of the chart data.Since most graphs of the efficient frontier look better withmore points near the minimum variance point, the chart datais generated using that assumption. The graph will alsoauto-scale itself based on the data. An example of the graphoutput is in Figure 10.

Figure 7. ChartData Sheet.

When you click “Run Program” the date selectiondialog box pops up:

Figure 8. Date Selection Dialogue Box.

Use the mouse to select the desired start and end dates.There are several checks built into the dialog box such asthe date cannot be in the future and the start date must be

before the end date. Once again, the user is reminded that aminimum of 30 observations must be used. Once the datesare selected and the OK is clicked, a number of observationscheck dialog box is displayed:

Figure 9. Download Initiation Selection Box.

If the user clicks "No" they are returned to the datedialog box. After clicking “Yes”, the user can watch the

Page 4: Automating Markowitz

8/10/2019 Automating Markowitz

http://slidepdf.com/reader/full/automating-markowitz 4/21

Journal of I nstru ctional Techniques in F in ance Volu me 4, Number 1, 2012

12

status bar as the program attempts to download the datafrom Yahoo and complete the optimization. Oncecompleted, the "Graph" sheet is shown:

Figure 10. Markowitz Graph Output.

SUGGESTED CLASSROOM USE

UW-Whitewater runs two sections of undergraduateInvestments each term with section sizes between 55 and 60students. Lectures and exams cover the optimization processfor two risky assets when r f exists, but the undergraduatestudents rarely show a thorough understanding of themethod. One way to increase their understanding is use realworld data with more than 2 risky assets. Withundergraduates, 5 risky assets is sufficient to determine ifthey can complete the optimization process and graph an

efficient frontier. Instructions on how to download datafrom finance.yahoo.com are provided to students along withinstructions on how to optimize a portfolio using Solver inExcel. Some textbooks contain this information but theundergraduate textbook currently in use at UW-Whitewaterdoes not. The yahoo instructions are also available from theauthors and instructions for Markowitz style optimizationare available in many textbooks including Bodie, Kane andMarcus, Investments (McGraw-Hill Irwin), Chapter 7,Appendix A (pages 234-239 in the 9th edition) and CraigHolden's Excel Modeling and Estimation in Investments (Pearson Prentice Hall), Chapters 7 and 8 in the 3rd edition.

When class sizes were smaller (in the early 1990'sInvestment class sizes at Whitewater were in the 15-20range), students were put into small groups and allowed toselect their own securities. Grading of 6-10 assignments wasnot prohibitive and assignments could be returned in a fewdays. As class sizes grew, group sizes increased but classsizes reached the point where it was necessary to assign asingle group of specific stocks for the entire class. Theseadjustments allowed the grading of 30-40 assignments in areasonable timeframe but dramatically increased the amount

of cross-group sharing. Requesting students behave ethicallyand increasing the penalty for cross-group sharing had littleeffect on the issue. It was clear that each group or studentwould need to be assigned a separate set of securities andthat a more efficient method of grading had to be used; thusthe development of the automated program.

We use the program for several purposes. For theundergraduates, we randomly generate the 5 stock portfoliosthey use in their optimizations. If there are 40 groups, werandomly generate 40 5-stock portfolios. Since the programgenerates the graph for the random securities, it is possibleto determine if the optimization produces a "good" graph

before including that set of securities in the assignment.Students may select different points for their optimizationthan the automated program but the program significantlyreduces the time needed to determine if the optimizationwas done correctly. For the graduates, they select their own10 stock portfolios, but those assignments can still be easilygraded since the program can download and optimize a 10stock portfolio in about 5 seconds (assuming a reasonable

Internet connection). We also use the program in-class todemonstrate to students that most efficient frontiers have asimilar shape regardless of the time period, length of time

period or number of securities in the portfolio. We find thatsimply discussing correlations and their relationship to theshape of the efficient frontier is never as effective as usingreal data. The students also get to see the entire processfrom hand calculating a two stock optimization, to usingExcel to optimize a multi-stock portfolio, to a fullyautomated computation of an optimization.

Assigning every group their own set of stocks hassignificantly reduced the amount of cross-group sharing butfree-riding within groups still exists. Use of student self-evaluations within the group helps with this issue but oftenstudents are more concerned they will have to work with thesame student again in another class than they are with theethics of giving another student a good evaluation whenthey were actually a free-rider.

CONCLUSION

This paper described the "front-end" of an automated program for generating efficient frontiers using MicrosoftExcel. The program can be used to reduce grading timewhile significantly curtailing cross-group sharing (cheating)when professors use assignments to improve studentunderstanding of Markowitz (1959) optimizations. The

program is freely available by contacting David Porter in theUW-Whitewater Department of Finance and Business Law.To minimize the distribution of the program to students,

professors should use either their office phone or universityemail as evidence they are not a student. For those familiarwith VBA, a PDF file is also available which explains thecode thereby reducing the learning curve if changes aredesirable to meet the individual needs of a specific course.To view the VBA code in the Excel file, use Alt+F11 or use

Page 5: Automating Markowitz

8/10/2019 Automating Markowitz

http://slidepdf.com/reader/full/automating-markowitz 5/21

Journal of I nstru ctional Techniques in F in ance Volu me 4, Number 1, 2012

13

the Developer tab. The Developer tab is not shown on theExcel toolbar by default but can be turned on by going toFile | Options | Customize Ribbon and checking“Developer” under the “Main Tabs” . The code can then beviewed by clicking on "View Code":

Figure 11. Developer Tab.

The main code can be found by double clicking on theModule "MainApplication":

Figure 12. Main Application Location.

REFERENCE

Markowitz, H. (1959). Portfolio Selection: Efficient Diversification of Investments , John Wiley & Sons, NewYork City, New York.

David Porter is a Professor of Finance at the University ofWisconsin-Whitewater.

Robert Stretcher is a Professor of Finance at Sam HoustonState University.

The Securities and Exchange Commission (SEC) hasupgraded the existing system of Electronic Data Gathering,

Analysis, and Retrieval (EDGAR), to Interactive Data Electronic Applications (IDEA) platform, using eXtensible Business Reporting Language (XBRL). In January 2009, theSEC issued its final mandate for XBRL adoption and theconversion target dates for all firms. This conversionenables users to retrieve listed companies’ financial

statement information at two levels, document and the dataelement, compared to the document level alone under theexisting system of EDGAR. Beneficial to all users, thischange is particularly important to resource-strappedentities such as small businesses, and universities. Theresult is user friendly for students researching information.With simply the industry standard tool of Microsoft Excel,without expensive proprietary XBRL modules, detailedinformation is now available, quickly, efficiently and atminimum cost. This paper explains the basic concept of

XBRL and demonstrates the ease of the retrieval process in Microsoft Excel.

INTRODUCTION

On January 30, 2009, the Securities and ExchangeCommission (SEC) chairperson, Mary Schapiro, announcedthe agency’s final mandate for eXtensible BusinessReporting Language (XBRL) adoption and the firmconversion target dates (SEC, January 2009). The mandatestated the largest domestic and foreign public companiesthat use U.S. Generally Accepted Accounting Principles(GAAP) to file their financial statements in XBRL format

by June 15, 2009 1: medium-sized filers by June 15, 2010,and the rest of the filers, either using U.S. GAAP orInternational Financial Reporting Standards (IFRS), by June15, 2011 (Fang 2010).

The new rules are intended to make financial

information easier for investors to analyze and to assist inautomating regulatory filings and business information

processing. The XBRL system lists information at both thedocument level, such as the entire set of financial statementsfor a given firm, and the data element level, such asindividual accounts like inventory. In XBRL interactivedata, or data tagged at the data element level, can functionacross multiple and/or different platforms or application

programs. Thus, XBRL has the potential to increase thespeed, accuracy and usability of financial disclosure, and

Demonstrating Retrieval ofFinancial Information inXBRL

Ameeta Jaiswal-Dale and Ji anin g(Jade) F ang

Page 6: Automating Markowitz

8/10/2019 Automating Markowitz

http://slidepdf.com/reader/full/automating-markowitz 6/21

1

Automating Markowitz with VBA: An Explanation

This file describes the functions and subroutines used to gather the data and generate the optimizationfor the application titled "Automating Markowitz Optimizations using VBA". We use one form to request

the start and end dates for the data that is not described below. The object is not to teach VBAprogramming but to provide some guidance to expedite the learning curve for those wishing to alter thecode for their own needs. We would describe the code below as "typical" code that can be found online orin a good VBA book such as Power Programming with VBA by John Walkenback or VBA and Macros forMicrosoft Excel by Bill “MrExcel” Jelen and Tracy Syrstad.

The code begins with “Option Explicit” requiring that all variables be explicitly declared. Option Base 1starts the array counters at 1 instead of the default 0. Most of the variables are declared as Public so theyare usable in all subroutines:

Option ExplicitOption Base 1

Public nTickers As Long, nSymbols As Long, nRandom As LongPublic nSamples As Long, i As Long, j As Long, k As Long, MinObs As LongPublic Symbols() As String, Symb As String, LegalName() As StringPublic IndexSymbol As String, IndexName As String, maxTicker As StringPublic LendRate As Single, BorrowRate As Single, RiskAversion As SinglePublic EndMonth As Integer, EndDay As Integer, EndYear As IntegerPublic StartMonth As Integer, StartDay As Integer, StartYear As IntegerPublic nMonths As Integer, nRuns As IntegerPublic EndDate As Date, StartDate As DatePublic Msg As String, Ans As VariantPublic maxStdDev As Double, maxReturn As Double, MVPStdDev As DoublePublic MinVar As Boolean, Frontier As Boolean, AllowShort As BooleanPublic Error As BooleanPublic FormatRange As Range

The main application starts with additional variable declarations and then sets the display alerts andscreen updating to false (turns them off). Since the program updates the status bar, it is turned on:

Sub MainApplication()

Dim Msg As String, Ans As StringDim ColNum As Long, nRow As LongDim UsedRow() As IntegerDim SheetName As WorksheetDim Duplicate As Boolean

On Error Resume Next

Application.DisplayAlerts = FalseApplication.ScreenUpdating = FalseApplication.DisplayStatusBar = True

Although it is personal preference, we prefer to start everything from a blank slate so we delete previousworksheets:

If SheetExists("Prices") Then Sheets("Prices").DeleteSheets.Add after:=Sheets("Stocks")ActiveSheet.Name = "Prices"

Page 7: Automating Markowitz

8/10/2019 Automating Markowitz

http://slidepdf.com/reader/full/automating-markowitz 7/21

2

If SheetExists("Returns") Then Sheets("Returns").DeleteSheets.Add after:=Sheets("Prices")ActiveSheet.Name = "Returns"If SheetExists("BasicStats") Then Sheets("BasicStats").DeleteSheets.Add after:=Sheets("Returns")ActiveSheet.Name = "BasicStats"If SheetExists("Model") Then Sheets("Model").DeleteSheets.Add after:=Sheets("BasicStats")ActiveSheet.Name = "Model"If SheetExists("ChartData") Then Sheets("ChartData").DeleteSheets.Add after:=Sheets("Model")ActiveSheet.Name = "ChartData"If SheetExists("Graph") Then Sheets("Graph").Delete

In this example, we either randomly generate the stocks to be used in the optimization by selecting themfrom the worksheet “Stocks” or use the supplied symbols. First, check to see if the random value in cellF1 is set to zero and if it is then count the number of tickers in column A, check that there are between 2and 20 tickers, redimension the arrays for the number of tickers and read in the tickers:

nRandom = Range("F1").ValueIf nRandom = 0 Then

nTickers = Cells(Rows.Count, 1).End(xlUp).RowIf nTickers < 2 Or nTickers > 20 Then

Msg = "You must have between 2 and 20 tickers. Either the " & vbCrLf & _"rando m number of tickers must be between 2 and 20, " & vbCrLf & _"or the number of tickers must meet the same criteria."Ans = MsgBox(Msg, vbOKOnly + vbExclamation)Exit Sub

End IfReDim UsedRow(nTickers)ReDim Symbols(nTickers)For i = nTickers To 1 Step -1

Symbols(i) = Range("A1").Offset(i - 1, 0).Value

Next iEnd If

If the random value in cell F1 is not zero, then check that the random value is between 2 and 20 andredimension the arrays for the appropriate number:

If nRandom <> 0 ThennTickers = nRandomIf nTickers < 2 Or nTickers > 20 Then

Msg = "You must have between 2 and 20 tickers. Either the " & vbCrLf & _"random number of tickers must be between 2 and 20, " & vbCrLf & _"or the number of tickers must meet the same criteria."

Ans = MsgBox(Msg, vbOKOnly + vbExclamation)Exit SubEnd IfReDim UsedRow(nTickers)ReDim Symbols(nTickers)

Count the number of security symbols on the "Stocks" sheet and make sure it is greater than the randomnumber in cell F1:

Sheets("Stocks").Select

Page 8: Automating Markowitz

8/10/2019 Automating Markowitz

http://slidepdf.com/reader/full/automating-markowitz 8/21

3

nSymbols = Cells(Rows.Count, 1).End(xlUp).RowIf nSymbols < nTickers Then

Msg = "The number of security symbols on the Stock sheet " & vbCrLf & _"is less than the random number in Cell F1. Please add " & vbCrLf & _"more security symbols or reduce the random number."Ans = MsgBox(Msg, vbOKOnly + vbExclamation)Exit Sub

End If

Initialize the random number generator and create an array of random numbers:

RandomizeFor i = 1 To nTickers

DonRow = Int(Rnd() * nSymbols)

If nRow = 0 Then nRow = 1

Duplicates are not allowed, so check to see if the random ticker has been used before. If it has, generatea new random number:

Duplicate = FalseFor j = 1 To iIf UsedRow(j) = nRow Then Duplicate = True

Next jLoop While DuplicateUsedRow(i) = nRowSymbols(i) = Sheets("Stocks").Range("A1").Offset(nRow, 0).Value

Next iEnd If

Stock tickers can be illegal names for named ranges, so set up a set of legal names:

ReDim LegalName(nTickers)

For i = 1 To nTickersLegalName(i) = "Stock" & i

Next i

Clear a spot for data on the "Explanation" sheet:

With Sheets("Explanation")Range("AA1:AG65500").ClearEnd With

S et the “Error” to False before calling the GetYahooData Subroutine (this routine accessesfinance.yahoo.com and downloads the data) and then check the “Error” after the subroutine to see if there

was an error accessing the data. If there was, exit the program:Error = FalseCall GetYahooData(ColNum)If Error Then

Sheets("Explanation").SelectRange("F1").SelectExit Sub

End If

Page 9: Automating Markowitz

8/10/2019 Automating Markowitz

http://slidepdf.com/reader/full/automating-markowitz 9/21

4

Although it is personal preference, it might be useful to let the user know if there was an issue trying todownload a particular ticker:

If ColNum < nTickers ThenMsg = "Yahoo reports that the ticker '" & Symb & "' does not exist," & vbCrLf & _"or does not exist for the specified dates." & vbCrLf & _"This may be from an internet error or a ticker symbol error." & vbCrLf & _"If you are sure that this ticker symbol is correct then you should" & vbCrLf & _"close Excel then try running the program again."Ans = MsgBox(Msg, vbOKOnly + vbExclamation)Sheets("Explanation").SelectExit Sub

End If

Call the Returns subroutine (to compute the returns). Again if there is an error, exit the program:

Call ReturnsIf Error Then Exit Sub

Create named ranges for the returns for each of the stocks:

For k = 1 To nTickersSheets("Returns").Range("A2").Offset(0, k - 1).Resize(MinObs).Name = LegalName(k)

Next k

Call each of the subroutines need to compute and chart the optimization:

Call GetStatsCall ModelSheet_SetupCall ChartDataSheet_SetupIf Error Then Exit SubCall UpdateChart

The main program finishes by turning on the display alerts and screen updating, then clears the statusbar and ends the subroutine:

Application.DisplayAlerts = TrueApplication.ScreenUpdating = TrueApplication.StatusBar = Empty

End Sub

The main program calls t he function “SheetExists” and returns TRUE if a worksheet exists in the activeworkbook. As noted above, if the sheet exists, we delete it so we always start from a clean slate:

Public Function SheetExists(sname) As BooleanDim x As Object

On Error Resume NextSet x = ActiveWorkbook.Sheets(sname)If Err.Number = 0 Then SheetExists = True _

Else SheetExists = FalseEnd Function

The web query to download data from Yahoo can be found in Bill Jelen’s book refer enced above. Weplace the web query in the subroutine GetYahooData. The subroutine begins with variable declarationsand then opens the form “frmGetDates”. This form requests the start and end dates for the data and doesa few checks such as making sure the end date is not before the begin date and the end date is not in the

Page 10: Automating Markowitz

8/10/2019 Automating Markowitz

http://slidepdf.com/reader/full/automating-markowitz 10/21

5

future. You could just as easily put the dates in cells on the “Explanation” sheet but the form is a bit moreprofessional:

Sub GetYahooData(ColNum As Long)Dim LastRow As LongDim FreqS As String

Show (display) the form GetDates to the user so that they can select the start and end dates for the data:

frmGetDates.ShowIf Error Then Exit Sub

For simplicity, we only use monthly data in this example. We also need to adjust the information passedfrom the form GetDates so it matches what is expected by Yahoo (January is month 0 and Decembermonth 11):

FreqS = "m"StartMonth = StartMonth - 1EndMonth = EndMonth - 1

Then for each ticker, we request the selected data from Yahoo. The counter “ColNum” is set to zero forstarters:

ColNum = 0For i = 1 To nTickers

Sheets("Explanation").SelectRange("AA1:AG65000").ClearSymb = Symbols(i)Application.StatusBar = "Attempting to Download " & Symb & " ..."With ActiveSheet.QueryTables.Add(Connection:= _

"URL;http://ichart.finance.yahoo.com/table.csv?s=" & Symb & _"&a=" & StartMonth & "&b=" & StartDay & "&c=" & StartYear & _"&d=" & EndMonth & "&e=" & EndDay & "&f=" & EndYear & "&g=" & FreqS _

, Destination:=Range("AA1")).RefreshStyle = xlOverwriteCells.RowNumbers = False.FillAdjacentFormulas = False.BackgroundQuery = True.SavePassword = False.SaveData = True.AdjustColumnWidth = False.RefreshPeriod = 0.Refresh BackgroundQuery:=False.WebSelectionType = xlSpecifiedTables.WebTables = "19"

Most of the options, RefreshStyle, etc., are not needed for the query to work but are usually kept forclarity. We have run the query with only the last three options. A complete list of options is available at theMicrosoft Developer Library: http://msdn.microsoft.com/en-us/library/microsoft.office.interop.excel.querytable_properties.aspx . Note that the WebTables page of 19requires either some good guessing or an examination of the source code at Yahoo. There is no directway to determine the correct web table to download so if Yahoo moves where the table is located, thisnumber will need to be changed.

The data is downloaded as comma delimited so it must be parsed into columns. There are many ways todo this but the example below is relatively efficient:

Page 11: Automating Markowitz

8/10/2019 Automating Markowitz

http://slidepdf.com/reader/full/automating-markowitz 11/21

6

Sheets("Explanation").Range("AA1").SelectRange(Selection, Selection.End(xlDown)).SelectSelection.TextToColumns Destination:=Range("AA1"), DataType:=xlDelimited, _TextQualifier:=xlDoubleQuote, ConsecutiveDelimiter:=True, Tab:=False, _Semicolon:=False, Comma:=True, Space:=False, Other:=False, FieldInfo:= _Array(Array(1, 1), Array(2, 1), Array(3, 1), Array(4, 1), Array(5, 1), Array(6, 1), _Array(7, 1)), TrailingMinusNumbers:=True

End With

If the query was successful, increment the counter:

ColNum = ColNum + 1

Determine how much data was downloaded and copy the closing prices to the prices sheet:

LastRow = Sheets("Explanation").Cells(Rows.Count, 27).End(xlUp).RowSheets("Prices").Range("A1").Offset(0, ColNum) = Symbols(i)Sheets("Explanation").Range("AG1:AG" & LastRow).Copy _

Sheets("Prices").Range("A2").Offset(0, ColNum) Next i

After downloading the data for each of the tickers, copy the dates to the “Prices ” sheet, set the columnwidth and set the correct date format:

Sheets("Explanation").Range("AA1:AA" & LastRow).CopySheets("Prices").Range("A2").PasteSpecial (xlPasteValues)Sheets("Prices").Columns("A:A").ColumnWidth = 12.86Selection.QueryTable.Delete

Set the correct date format on worksheet "Prices":

With Sheets("Prices")

.ActivateRange("A1").SelectIf FreqS = "m" Then .Range("A3:A" & LastRow).NumberFormat = "[$-409]mmm-yy;@"

Add some other formatting:

Range("B3").Resize(LastRow, nTickers).NumberFormat = "0.00"With Range("A1").Resize(2, nTickers + 1)

.Interior.Color = 13209

.Font.ThemeColor = xlThemeColorDark1

.Font.Bold = True

.HorizontalAlignment = xlCenter

.VerticalAlignment = xlCenter

End WithRange("A1").Interior.ColorIndex = xlNoneWith Range("A2:A" & LastRow)

.Font.ThemeColor = xlThemeColorDark1

.Interior.Color = 13209

.Font.Bold = True

.HorizontalAlignment = xlCenter

.VerticalAlignment = xlCenterEnd With

End With

Page 12: Automating Markowitz

8/10/2019 Automating Markowitz

http://slidepdf.com/reader/full/automating-markowitz 12/21

7

To complete the subroutine, clear the data range and turn off copy mode:

Sheets("Explanation").Range("AA1:AG65500").ClearApplication.CutCopyMode = False

End Sub

The “Returns” subroutine computes returns from the prices downloaded from Yahoo. It starts with anadditional variable declaration and sets the Symb variable to null (blank).

Sub Returns()

Dim LastRow As LongSymb = ""

Determine the minimum number of observations for each ticker:

For i = 1 To nTickersLastRow = Sheets("Prices").Cells(Rows.Count, i + 1).End(xlUp).Row - 2If i = 1 Then

MinObs = LastRowElse

If LastRow < MinObs ThenMinObs = LastRowSymb = Symbols(i)

End IfEnd If

Next i

Returns have one less observation than prices:

MinObs = MinObs - 1

If a symbol has fewer observations than the others, report that symbol to the user:

If Not Symb = "" ThenMsg = "Minimum number of observations is " & MinObs & " for symbol " & SymbAns = MsgBox(Msg, vbOKOnly)

End If

If a symbol has fewer than 30 observations, report that to the user, set the Error to true, delete theBasicStats worksheet and exit the subroutine:

If MinObs < 30 ThenMsg = "Minimum number of observations is less than 30" & Chr(10) & _"Terminating the application."

Ans = MsgBox(Msg, vbOKOnly)Error = TrueIf SheetExists("BasicStats") Then Sheets("BasicStats").DeleteSheets("Explanation").SelectExit Sub

End If

On the Returns sheet, insert the ticker symbols for the titles:

Sheets("Returns").Select

Page 13: Automating Markowitz

8/10/2019 Automating Markowitz

http://slidepdf.com/reader/full/automating-markowitz 13/21

8

For i = 1 To nTickersRange("A1").Offset(0, i - 1) = Symbols(i)

Next i

Compute the returns:

Range("A2") = "=(Prices!B3-Prices!B4)/Prices!B4*100"Range("A2").AutoFill Destination:=Range("A2").Resize(, nTickers), Type:=xlFillDefaultRange("A2").Resize(, nTickers).AutoFill Destination:=Range("A2").Resize(MinObs, nTickers),Type:=xlFillDefault

Add some formatting:

Range("A2").Resize(MinObs, nTickers).NumberFormat = "0.0000"Set FormatRange = ActiveSheet.UsedRange.SpecialCells(xlCellTypeConstants, 2)With FormatRange

.Interior.Color = 13209

.Font.ThemeColor = xlThemeColorDark1

.Font.Bold = True

.HorizontalAlignment = xlCenter

.VerticalAlignment = xlCenterEnd With

End Sub

The “ GetStats ” subroutine computes the basic statistics from the returns.

Sub GetStats()

The subroutine starts by selecting the BasicStats worksheet, then adds some formatting and the titles:

Sheets("BasicStats").SelectRange("A:A").ColumnWidth = 12

Range("A1:D1").MergeCells = TrueRange("A1").Value = "Summary Measures for Stock Returns"Range("A3").Value = "Stocks"Range("A4").Value = "Means"Range("A5").Value = "Std. Dev."Range("A6").Value = "Min"Range("A7").Value = "Max"Range("A8").Value = "Range"Range("A10").Value = "Correlations"

For each ticker, enter the formulas for the average, standard deviation, minimum, maximum and range ofreturns:

For i = 1 To nTickersRange("A3").Offset(0, i).Value = Symbols(i)Range("A4").Offset(0, i).Formula = "=Average(" & LegalName(i) & ")"Range("A5").Offset(0, i).Formula = "=Stdev(" & LegalName(i) & ")"Range("A6").Offset(0, i).Formula = "=Min(" & LegalName(i) & ")"Range("A7").Offset(0, i).Formula = "=Max(" & LegalName(i) & ")"Range("A8").Offset(0, i).FormulaR1C1 = "=R[-1]C-R[-2]C"

Next

Insert the titles (symbols) for the correlation table:

Page 14: Automating Markowitz

8/10/2019 Automating Markowitz

http://slidepdf.com/reader/full/automating-markowitz 14/21

9

With Range("A10")For i = 1 To nTickers

With .Offset(i, 0).Value = Symbols(i)

End WithWith .Offset(0, i)

.Value = Symbols(i)End With

Next

Compute the correlations:

For i = 1 To nTickersFor j = 1 To nTickers

.Offset(i, j).Formula = "=Correl(" & LegalName(i) & "," & LegalName(j) & ")" Next

NextEnd With

Add some formatting:

Range("B4").Resize(nTickers + 7, nTickers).NumberFormat = "0.0000"Set FormatRange = ActiveSheet.UsedRange.SpecialCells(xlCellTypeConstants, 2)With FormatRange.Interior.Color = 13209.Font.ThemeColor = xlThemeColorDark1.Font.Bold = True.HorizontalAlignment = xlCenter.VerticalAlignment = xlCenterEnd With

End Sub

The ModelSheet_Setup subroutine sets up the model worksheet with the necessary data to optimize theportfolio and calls the subroutine "Solver" to perform the optimization:

Sub ModelSheet_Setup()

Dim modelSheet As WorksheetSet modelSheet = Worksheets("Model")Application.StatusBar = "Calculating Covariances"

With modelSheet.Activate

Add some formatting and setup the titles:.Columns("A:A").ColumnWidth = 12.Columns("B:F").ColumnWidth = 9.29.Range("A1:C1").MergeCells = True.Range("A1").Value = "Portfolio Selection Model"With .Range("A3")

.Value = "Stock"

.Offset(1, 0).Value = "Weights"

.Offset(2, 0).Value = "Means"

Page 15: Automating Markowitz

8/10/2019 Automating Markowitz

http://slidepdf.com/reader/full/automating-markowitz 15/21

10

For i = 1 To nTickers.Offset(0, i).Value = Symbols(i)

Enter the naive weights as a starting point for the optimizations:

.Offset(1, i).Value = 1 / nTickers

Enter the average returns as the expected returns:

.Offset(2, i).Formula = "=Average(" & LegalName(i) & ")" Next

End With

Create named ranges for the weights and expected returns:

With .Range("A4")Range(.Offset(0, 1), .Offset(0, 1).End(xlToRight)).Name = "Weights"Range(.Offset(1, 1), .Offset(1, 1).End(xlToRight)).Name = "Means"

End With

Sum the weights, add a title, and create a named range:

With .Range("A3").Offset(0, nTickers + 1).Value = "Sum".Offset(1, 0).Name = "SumWeights".Offset(1, 0).Formula = "=Sum(Weights)"

End With

Add the titles for the covariance table:

With .Range("A7").Value = "Covariances"For k = 1 To nTickers

.Offset(k, 0).Value = Symbols(k)

.Offset(0, k).Value = Symbols(k) Next k

Compute the covariances:

For i = 1 To nTickersFor j = 1 To nTickers

.Offset(i, j).Formula = "=Covar(" & LegalName(i) & "," & LegalName(j) & ")" Next

NextRange(.Offset(1, 1), .Offset(nTickers, nTickers)).Name = "Covar"

End With

With .Range("A7").Offset(nTickers + 2, 0)

Add the title for the expected return on the portfolio and enter the formula to compute its value:

.Value = "E(Rp)"

.Offset(0, 1).Name = "ERp"

.Offset(0, 1).Formula = "=Sumproduct(Weights,Means)"

Page 16: Automating Markowitz

8/10/2019 Automating Markowitz

http://slidepdf.com/reader/full/automating-markowitz 16/21

11

Add the title for the target cell and set its starting value to zero. The target cell is used as the target for theoptimization

.Offset(0, 3).Value = "Target"

.Offset(0, 4).Value = 0

.Offset(0, 4).Name = "Target"

Add the title for the variance of the portfolio and enter the formula to compute its value:

.Offset(2, 0).Value = "Var(Rp)"With .Offset(2, 1)

.FormulaArray = "=MMult(Weights,MMult(Covar,Transpose(Weights)))"

.Name = "VarRp"End With

Add the title for the standard deviation of the portfolio and enter the formula to compute its value:

.Offset(2, 3).Value = "Sigma(Rp)"With .Offset(2, 4)

.Formula = "=Sqrt(VarRp)"

.Name = "SigmaP"End With

End WithEnd With

Add some formatting and end the subroutine:

Range("B4").Resize(nTickers + 8, nTickers + 1).NumberFormat = "0.0000"Set FormatRange = ActiveSheet.UsedRange.SpecialCells(xlCellTypeConstants, 2)With FormatRange

.Interior.Color = 13209

.Font.ThemeColor = xlThemeColorDark1

.Font.Bold = True

.HorizontalAlignment = xlCenter

.VerticalAlignment = xlCenterEnd With

End Sub

The subroutine ChartDataSheet is used to call RunSolver and record the solutions for each optimizationon the ChartData sheet for charting. It starts with some additional variable declarations:

Sub ChartDataSheet_Setup()

Dim CDSheet As WorksheetDim nErr As IntegerDim MVPRet As Double, Temp1 As Double, Temp2 As Double

Dim SameReturn As BooleanDim T1 As Double, T2 As Double, T3 As Double

Set CDSheet = Worksheets("ChartData")

Add some formatting and titles:

With CDSheet.Select.Cells.ClearContents

Page 17: Automating Markowitz

8/10/2019 Automating Markowitz

http://slidepdf.com/reader/full/automating-markowitz 17/21

12

.Range("A:Z").Font.Bold = False

.Columns("A:A").ColumnWidth = 15.14

.Columns("B:B").ColumnWidth = 12.14

.Columns("C:C").ColumnWidth = 10.86

.Range("A1").Value = "Efficient Frontier"

.Range("B2").Value = "Port. Std.Dev."

.Range("C2").Value = "Port. Return"

.Range("E1").Value = "Optimal Portfolio Weights"

.Range(Cells(1, 5), Cells(1, nTickers + 4)).MergeCells = TrueWith .Range("D2")

For k = 1 To nTickers.Offset(0, k).Value = Symbols(k)

NextEnd With

End With

Find the highest return and the matching standard deviation - the endpoint of the efficient frontier:

With Sheets("BasicStats").ActivateFor i = 1 To nTickers

If i = 1 ThenmaxReturn = Range("A4").Offset(0, i).ValuemaxStdDev = Range("A5").Offset(0, i).ValuemaxTicker = Range("A3").Offset(0, i).Value

ElseIf maxReturn < Range("A4").Offset(0, i).Value ThenmaxReturn = Range("A4").Offset(0, i).ValuemaxStdDev = Range("A5").Offset(0, i).ValuemaxTicker = Range("A3").Offset(0, i).Value

End IfEnd If

Next i

End With

Number of times to run Solver to get a "nice" graph:

nRuns = 15

Compute the minimum variance portfolio:

Range("Target") = 0Call RunSolver

Copy the expected return, standard deviation and weights to the appropriate cells:

With CDSheet.Range("A" & nRuns + 3).Value = "M-V Portfolio".Offset(0, 1).Value = Range("SigmaP").Value.Offset(0, 2).Value = Range("ERp").Value.Offset(0, 2).Name = "MVPReturn"For k = 4 To nTickers + 3

.Offset(0, k).Value = Range("Weights").Cells(k - 3).Value Next

End WithMVPRet = Range("MVPReturn").Value

Page 18: Automating Markowitz

8/10/2019 Automating Markowitz

http://slidepdf.com/reader/full/automating-markowitz 18/21

13

MVPStdDev = Range("SigmaP").Value

Use an exponential function to select more points near the minimum variance point. The divisor "T1"should scale between 0 and 1. The 1.5 is arbitrary and is used to get the graph to look appropriate:

T1 = 1 / Exp(1 / 1.5)

Run Solver "nRuns" times and record the results. Solver can make errors, so set the error counter to zeroand increment with each error:

For i = nRuns - 1 To 1 Step -1nErr = 0

Set the target cell for each optimization by altering the standard deviation between the minimum variancepoint and the maximum standard deviation:

T2 = 1 / Exp((i + 1) / 1.5)T3 = T2 / T1Range("Target") = MVPStdDev + T3 * (maxStdDev - MVPStdDev)

Run Solver with the new target cell. Since Solver can make errors, check for an error after each run andre-run if necessary:

DoCall RunSolverWith CDSheet.Range("B4")

.Offset(i - 1, 0).Value = Range("SigmaP").Value

.Offset(i - 1, 1).Value = Range("ERp").ValueEnd With

Make sure Solver did not get stuck and return an inferior point:

SameReturn = False

Temp1 = Round(CDSheet.Range("C4").Offset(i - 1, 0).Value, 4)Temp2 = Round(CDSheet.Range("C4").Offset(i, 0).Value, 4)If Temp1 <= Temp2 Then

SameReturn = TrueSheets("Model").Range("B4").Resize(, nTickers).Value = 0nErr = nErr + 1If nErr = 1 Then Sheets("Model").Range("B4") = 1If nErr = 2 Then Sheets("Model").Range("C4") = 1If nErr = 3 Then Sheets("Model").Range("D4") = 1If nErr = 4 Then Sheets("Model").Range("E4") = 1If nErr = 5 Then Sheets("Model").Range("F4") = 1If nErr = 6 Then Sheets("Model").Range("B4").Resize(, nTickers).Value = 1/nTickers/2If nErr = 7 Then Sheets("Model").Range("B4").Resize(, nTickers).Value = 1/nTickers*2

If nErr > 7 ThenError = TrueMsgBox "Error in Solver Solution"Exit Sub

End IfEnd If

If the point is an inferior point, run Solver again with the same target cell:

Loop While Same Return

Page 19: Automating Markowitz

8/10/2019 Automating Markowitz

http://slidepdf.com/reader/full/automating-markowitz 19/21

14

Write the optimal weights for each stock for each for each run:

With CDSheet.Range("D4")For k = 1 To nTickers

.Offset(i - 1, k).Value = Range("Weights").Cells(k).Value Next

End With Next i

The maximum return is the last point on the efficient frontier:

CDSheet.Range("B3").Value = maxStdDevCDSheet.Range("C3").Value = maxReturnWith CDSheet.Range("D2")

For k = 1 To nTickersIf .Offset(0, k).Value = maxTicker Then

.Offset(1, k).Value = 1Else

.Offset(1, k).Value = 0End If

NextEnd With

Add some formatting and end the subroutine:

CDSheet.ActivateWith Range("A1").Resize(nRuns + 3, nTickers + 4)

.NumberFormat = "0.0000"

.HorizontalAlignment = xlCenter

.VerticalAlignment = xlCenterEnd WithSet FormatRange = ActiveSheet.UsedRange.SpecialCells(xlCellTypeConstants, 2)

With FormatRange.Interior.Color = 13209.Font.ThemeColor = xlThemeColorDark1.Font.Bold = True

End WithEnd Sub

The subroutine RunSolver runs the Solver optimization routine:

Sub RunSolver()

Sheets("Model").Select

We include the code to run Solver directly but there can be conflicts if Solver is not correctly installed andreferenced. Application.Run does not have as many conflict issues as just using Solver but it runs slowerand is not as intuitive since the options are not referenced with words. For a good description of this issuesee http://peltiertech.com/Excel/SolverVBA.html . We start by resetting Solver:

SolverReset

The first constraint is that the weights must sum to 1. "Relation:=2" refers to "=":

SolverAdd CellRef:=Range("SumWeights"), _

Page 20: Automating Markowitz

8/10/2019 Automating Markowitz

http://slidepdf.com/reader/full/automating-markowitz 20/21

15

Relation:=2, _FormulaText:=1

The second constraint is that the portfolio standard deviation must equal the target cell:

SolverAdd CellRef:=Range("SigmaP"), _Relation:=2, _FormulaText:=Range("Target")

The maximization function will vary depending on the computation. In this example, we maximize theportfolio expected return by changing the weights. A "MaxMinVal=1" refers to maximizing:

SolverOk SetCell:=Range("ERp"), _MaxMinVal:=1, _ByChange:=Range("Weights")

In this example we run Solver with all the options set to the default, (there is no Solver options line). Sincethe Solver option AssumeNonNeg is set to True by default, it is not necessary to add the constraint thatthe weights are >= 0 when short selling is not allowed. A complete list of options along with their defaultvalues is available at the Microsoft Developer Library, http://msdn.microsoft.com/en-us/library/office/ff195446.aspx . You can also find references to the other Solver functions on the samepage.

Run the optimization. "UserFinish:=True" completes the optimization without asking if the user want tokeep the optimization:

SolverSolve UserFinish:=True

Reset the status bar to empty and end the subroutine:

Application.StatusBar = Empty

End Sub

The UpdateChart subroutine plots the chart data. It also starts with the declaration of a few variablesspecific to this subroutine:

Sub UpdateChart()

Dim minX As Single, maxX As SingleDim minY As Single, maxY As SingleDim xLength As Single, yLength As Single

Application.StatusBar = "Updating Chart"

Activate the chart data sheet, select the chart type, data range, location, style, labels format, axis

captions:Sheets("ChartData").ActivateActiveSheet.Shapes.AddChart.SelectActiveChart.ChartType = xlXYScatterSmoothActiveChart.SetSourceData Source:=Range("ChartData!$B$3:$C$" & nRuns + 3)ActiveChart.Location Where:=xlLocationAsNewSheetActiveChart.ChartStyle = 36ActiveChart.ClearToMatchStyleActiveChart.SetElement msoElementLegendNone

Page 21: Automating Markowitz

8/10/2019 Automating Markowitz

http://slidepdf.com/reader/full/automating-markowitz 21/21

16

ActiveChart.Location Where:=xlLocationAsNewSheetActiveChart.Axes(xlValue).TickLabels.NumberFormat = "0.00"ActiveChart.Axes(xlCategory).TickLabels.NumberFormat = "0.00"ActiveChart.SetElement (msoElementPrimaryCategoryAxisTitleAdjacentToAxis)ActiveChart.Axes(xlCategory, xlPrimary).AxisTitle.Caption = _"Standard Deviation of Portfolio Returns %"ActiveChart.SetElement (msoElementPrimaryValueAxisTitleRotated)ActiveChart.Axes(xlValue, xlPrimary).AxisTitle.Caption = "E(Rp) %"ActiveChart.Name = "Graph"Sheets("ChartData").Move Before:=Sheets("Graph")

Update the chart settings to improve chart spacing and end the subroutine:

minX = Application.WorksheetFunction.Min(Range("B3:B" & 18))maxX = Application.WorksheetFunction.Max(Range("B3:B" & 18))minY = Application.WorksheetFunction.Min(Range("C3:C" & 18))maxY = Application.WorksheetFunction.Max(Range("C3:C" & 18))xLength = maxX - minXyLength = maxY - minYSheets("Graph").SelectWith ActiveChart

With .Axes(xlCategory).MinimumScale = minX - 0.1 * xLength.MaximumScale = maxX + 0.1 * xLength

End WithWith .Axes(xlValue)

.MinimumScale = minY - 0.1 * yLength

.MaximumScale = maxY + 0.1 * yLengthEnd With

End WithApplication.StatusBar = Empty

End Sub