An Introduction To Excel VBA Programming With Applications in Finance and Insurance
An Introduction To Excel VBA Programming With Applications in Finance and Insurance
GUOJUN GAN
University of Connecticut
Storrs
CRC Press
Taylor & Francis Group
6000 Broken Sound Parkway NW, Suite 300
Boca Raton, FL 33487-2742
This book contains information obtained from authentic and highly regarded sources. Reasonable
efforts have been made to publish reliable data and information, but the author and publisher cannot
assume responsibility for the validity of all materials or the consequences of their use. The authors and
publishers have attempted to trace the copyright holders of all material reproduced in this publication
and apologize to copyright holders if permission to publish in this form has not been obtained. If any
copyright material has not been acknowledged please write and let us know so we may rectify in any
future reprint.
Except as permitted under U.S. Copyright Law, no part of this book may be reprinted, reproduced,
transmitted, or utilized in any form by any electronic, mechanical, or other means, now known or
hereafter invented, including photocopying, microfilming, and recording, or in any information
storage or retrieval system, without written permission from the publishers.
For permission to photocopy or use material electronically from this work, please access
www.copyright.com (http://www.copyright.com/) or contact the Copyright Clearance Center, Inc.
(CCC), 222 Rosewood Drive, Danvers, MA 01923, 978-750-8400. CCC is a not-for-profit organization
that provides licenses and registration for a variety of users. For organizations that have been granted
a photocopy license by the CCC, a separate system of payment has been arranged.
Trademark Notice: Product or corporate names may be trademarks or registered trademarks, and
are used only for identification and explanation without intent to infringe.
Visit the Taylor & Francis Web site at
http://www.taylorandfrancis.com
and the CRC Press Web site at
http://www.crcpress.com
To all my students.
Contents
List of Figures xi
Preface xv
I VBA Preliminaries 1
1 Introduction to VBA 3
1.1 Getting Started . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.2 Modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.3 VBA Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
1.4 The Excel Macro Recorder . . . . . . . . . . . . . . . . . . . . 13
1.5 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
2 Excel Objects 17
2.1 Excel Object Model . . . . . . . . . . . . . . . . . . . . . . . . 17
2.2 Application Object . . . . . . . . . . . . . . . . . . . . . . . . 21
2.3 Workbook Objects . . . . . . . . . . . . . . . . . . . . . . . . . 23
2.4 Worksheet Objects . . . . . . . . . . . . . . . . . . . . . . . . 28
2.5 Range Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
2.6 WorksheetFunction Object . . . . . . . . . . . . . . . . . . . 37
2.7 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
vii
viii Contents
II Applications 127
References 275
Index 278
xi
xii List of Figures
xiii
xiv List of Tables
11.1 Range Names Used by the Variable Annuity Inforce Tool . . . 217
Preface
xv
xvi Preface
Guojun Gan
Storrs, Connecticut, USA
Part I
VBA Preliminaries
1
Introduction to VBA
3
4 Introduction to VBA
If you use a Mac® computer, you can open the Visual Basic Editor by
clicking the menu item “Tools/Macro/Visual Basic Editor” (see Figure 1.3).
You can also click the “Editor” icon in the “Developer” tab to open the Visual
Basic Editor. If you do not see the “Developer” tab in your version of Excel,
1 New Ribbon interface has been used since Microsoft Office version 2007. In old versions
of Excel, you can open the Visual Basic Editor from the traditional menus.
Getting Started 5
The code is contained in a sub procedure, which starts with the keyword
sub and ends with the keyword End Sub. The name of the sub procedure
is “HelloWorld.” Line 2 is the VBA command that writes “Hello World!” to
the first cell of the first worksheet of the current workbook. Note that VBA
6 Introduction to VBA
code is case insensitive. The Visual Basic Editor changes the first letter of a
keyword to uppercase.
There are several ways to run the VBA macro. The first way to run the
VBA macro is to click the VBA code and press the key “F5.” After the code is
executed, the content of the first cell of the first worksheet will be changed
to “Hello World!"
A second way to run the VBA macro is to open the Macro dialog by
clicking “Macros” button in the left side of the “DEVELOPER” tab. On a
Mac computer, you can open the macro dialog by clicking “Tools/Macro/-
Macros.” The macro dialog is shown in Figure 1.6. You can select the macro
and click the “Run” button to run the macro. The Macro dialog also allows
you to set a shortcut key to your macro (See Exercise 1.1).
(a) Write a VBA sub procedure named HelloWorld2 that puts “Hello” in
the first cell in the first row of the second worksheet and “World” in the
second cell in the first row of the second worksheet.
(b) Assign a shortcut key ("Ctrl+Shift+A" on a Windows machine) to the sub
procedure and hit the shortcut key.
h
Getting Started 7
1.2 Modules
A module is a container for VBA code. A module can contain declarations
and procedures. An Excel application can call VBA code contained in one
or more modules to perform specified tasks.
Figure 1.7 shows a module displayed in the Visual Basic Editor. The
name of the module is MGetStarted. You can change the name of the
module in the Properties window, which is shown in the bottom left of the
figure.
As we can see from the figure, the module contains two declarations
and one procedure. The two declarations are:
1 Option Explicit
2 Dim nCount As Integer
The first statement contains two keywords: Option and Explicit. This
statement will force the explicit variable declaration, that is, you have to
declare a variable explicitly before using the variable. We will see some
examples of this statement in Section 3.1. The second statement declares
an integer variable named nCount.
A module can contain two types of procedures: sub procedures and
function procedures. A sub procedure contains VBA code to perform some
10 Introduction to VBA
There are two types of modules: standard modules and class modules.
If a module contains procedures that are not associated with any particular
object, then module is a standard module. If a module is associated with a
particular object, then it is a class module.
Class modules are related to object-oriented programming and are
different from standard modules in the way their data is stored. Only one
copy of a standard module’s data is stored. For example, if one part of
your program changes a public variable in a standard module and another
part of your program subsequently reads that variable, then it will get the
same value. Class module data is stored separately for each object. We will
introduce how to use class modules in Section 1.3.
Modules have the following advantages:
Exercise 1.2. Create a standard module and change its name to MTest.
21 sLastName = LastName
22 End Property
23
24 Public Property Get Grade () As String
25 Grade = sGrade
26 End Property
27
28 Public Property Let Grade ( ByVal Grade As String )
29 sGrade = Grade
30 End Property
31
32 Public Sub ShowInfo ()
33 MsgBox sFirstName & " " & sLastName & " 's grade
is " & sGrade
34 End Sub
The name of the class is the same as the name of the class module. From the
above code, we see that the class CStudent has three properties: FirstName,
LastName, and Grade. The class module also contains six property proce-
dures to set and get the values stored in the properties. The sub procedure
ShowInfo is used to display the property values in a message box.
To show how to use the class CStudent, we consider the following sub
procedure:
1 Sub ClassDemo1 ()
2 Dim cS1 As CStudent
3 Dim cS2 As CStudent
4
5 Set cS1 = New CStudent
6 cS1 . FirstName = " Graig "
7 cS1 . LastName = " Moyle "
8 cS1 . Grade = " A "
9
10 Set cS2 = New CStudent
11 cS2 . FirstName = " Violet "
12 cS2 . LastName = " Keen "
13 cS2 . Grade = " B "
14
15 cS1 . ShowInfo
16 cS2 . ShowInfo
17 End Sub
In the above code, we create two objects of the class CStudent by using
the keyword New. We assign the two objects to two variables by using the
The Excel Macro Recorder 13
keyword Set. Then we call the class’s method ShowInfo to display the
property values of the two objects.
Exercise 1.3. Create a VBA class named CBasket with the following two
properties: iCount and dPrice. The first property stores how many items
in the basket. The second property stores the price of each item. The class
also has a method named CalculateValue to calculate the total value of
the basket. Then write a sub procedure to test the class.
7 '
8 Range ( " D3 " ) . Select
9 ActiveCell . FormulaR1C1 = " Hello "
10 Range ( " D3 " ) . Select
11 Selection . Font . Bold = True
12 Selection . Borders ( xlDiagonalDown ) . LineStyle =
xlNone
13 Selection . Borders ( xlDiagonalUp ) . LineStyle =
xlNone
14 With Selection . Borders ( xlEdgeLeft )
15 . LineStyle = xlContinuous
16 . ColorIndex = 0
17 . TintAndShade = 0
18 . Weight = xlMedium
19 End With
20 With Selection . Borders ( xlEdgeTop )
21 . LineStyle = xlContinuous
22 . ColorIndex = 0
23 . TintAndShade = 0
24 . Weight = xlMedium
25 End With
The Excel Macro Recorder 15
In the above code, Lines 2–5 and 7 start with a single quote. These lines are
comments in VBA code.
The Excel macro recorder is useful in that it helps us figure out what
VBA commands we can use to do certain things, such as formatting a range.
However, the Excel macro recorder has some drawbacks. First, the Excel
macro recorder cannot create functions for us. It only produces sub proce-
dures. Second, the VBA code created by the recorder is not compact. It tends
to record more statements than we need. For example, we can write the
following sub procedure that does the same thing as the recorded macro:
1 Sub Macro1Manual ()
2 ' This macro does the same thing as Macro1
recorded by the Excel macro recorder
3 Range ( " D3 " ) . Select
4 ActiveCell . Value = " Hello "
5 ActiveCell . Font . Bold = True
6 Selection . Borders . LineStyle = xlContinous
7 Selection . Borders . Weight = xlMedium
8 End Sub
The macro written manually is much shorter than the recorded macro.
Exercise 1.4. Suppose that the range “A1:B2” contains four numbers. Use
16 Introduction to VBA
the Excel macro recorder to create a macro that does the following opera-
tions: select the range “A1:B2,” copy and paste the range to the range “F1:G2,”
and change the number format of the range “F1:G2” to a percentage with
two decimal places.
Exercise 1.5. Use the Excel macro recorder to record the following steps:
open the file “USLifeTable1999-2001Male.xls” by clicking “File/Open,”
select the range “A25:B134,” copy and paste the range to the range
“A1:B110” of a worksheet in your own workbook, and close the workbook
“USLifeTable1999-2001Male.xls.”
1.5 Summary
In this chapter, we introduced some basic concepts of VBA programming.
In particular, we introduced how to run VBA programs. We also introduced
VBA modules, including standard modules and class modules. Finally, we
introduced the Excel macro recorder, which is a useful tool to create VBA
commands.
There are quite a few books on VBA programming. For a brief history
of VBA, readers are referred to Urtis (2015). Readers who are beginners of
VBA programming are referred to Martin (2012), Walkenbach (2013b), and
Urtis (2015). Advanced readers are referred to Getz and Gilbert (2001) and
Bovey et al. (2009). For financial computing with Excel and VBA, readers are
referred to Lai et al. (2010).
2
Excel Objects
17
18 Excel Objects
Excel Application
Cells
shown in Figure 2.2. We can open the Object Browser by clicking the menu
item “View/Object Browser.”
We use the dot operator to access an object in the object hierarchy. To
reference a single object from a collection, we use the object’s name or index
number. In the following sub procedure, for example, we first assign the
text “A String” to the second cell in the first row of the first sheet. Then we
use the VBA function Debug.Print to display the value of that cell and the
name of the current workbook in the Immediate window.
1 Sub AccessObject ()
2 Application . ActiveWorkbook . Sheets (1) . Cells (1 , 2) .
Value = " A String "
3 Debug . Print Application . ActiveWorkbook . Sheets ( "
Sheet1 " ) . Cells (1 , 2) . Value
4 Debug . Print Application . ActiveWorkbook . Name
5 End Sub
If the sub procedure is executed, we see the following output in the Imme-
diate window:
1 A String
2 Book1
If the Immediate window is not shown in the Visual Basic Editor, you can
open the Immediate window by clicking the menu item “View/Immediate
Window.”
We can simplify the reference to an object in the object hierarchy by
omitting references to the assumed objects. If an object is the currently
active object, we can omit the reference to the object. For example, we can
rewrite the sub procedure AccessObject as follows:
1 Sub AccessObject2 ()
2 Sheets (1) . Cells (1 , 2) . Value = " A String "
3 Debug . Print ActiveWorkbook . Sheets ( " Sheet1 " ) . Cells
(1 , 2) . Value
4 Debug . Print ActiveWorkbook . Name
5 End Sub
In the above code, we first activate the first worksheet by calling the method
Activate. Then we call the method ClearContents to clear the content of
the cell at the second row and the second column. In Lines 4–5, we call the
VBA function Debug.Print to display the contents of the two cells in the
Immediate window. In Lines 6–7, we copy the content of the first cell to the
second cell and display the content of the second cell again. If we execute
the above code, we see the following output in the Immediate window:
1 Hello World !
2
3 Hello World !
h
Application Object 21
Property Description
ActiveCell Returns a Range object that represents
the active cell
ActiveSheet Returns an object that represents the active sheet
ActiveWorkbook Returns a Workbook object that represents the
workbook in the active window
ThisWorkbook Returns a Workbook object that represents the
workbook where the current macro code is running
DisplayAlerts True if Excel displays certain alerts and messages
while a macro is running. Read/write boolean
Read/write boolean
Columns Returns a Range object that represents all the
columns on the active worksheet
Rows Returns a Range object that represents all the rows
on the active worksheet
Name Returns a string that represents
the name of the object
Sheets Returns a Sheets collection that represents all
the sheets in the active workbook
Workbooks Returns a Workbooks collection that represents
all the open workbooks
FileDialog Returns a FileDialog object that represents
an instance of the file dialog
Executing the above code produces the following output in the Immediate
window:
1 Chapter1 . xlsm
2 Sheet1
3 $D$1
4 Microsoft Excel
Method Description
Evaluate Converts a Microsoft Excel name to
an object or a value
FindFile Displays the Open dialog box
GetOpenFilename Displays the standard Open dialog box and
gets a file name from the user
without actually opening any files
InputBox Displays a dialog box for user input and returns
the information entered in the dialog box
OnKey Runs a specified procedure when a particular key
or key combination is pressed
OnTime Schedules a procedure to be run at
a specified time in the future
Run Runs a macro or calls a function
The following sub procedure shows how to use the methods InputBox
and Evaluate:
1 Sub ApplicationDemo2 ()
2 Debug . Print Application . InputBox ( prompt := " Input a
cell address " , Type :=2)
3 Application . ActiveSheet . Range ( " B10 " ) . Value = 3.14
4 Debug . Print Application . Evaluate ( " B10 " ) . Value
5 End Sub
If we execute the above code, an input box will show up on the screen. If
we type “A1” (without the double quotes) in the input box, we will see the
following output in the Immediate window:
1 A1
2 3.14
Workbook Objects 23
The function Evaluate returned a Range object from the reference “B10.”
g
Exercise 2.4. Read the Application.InputBox Method (Excel) at https://
msdn.microsoft.com/en-us/library/office/ff839468.aspx and write
a sub procedure named InputBoxDemo to do the following: use the method
InputBox to let users input a number and then display the sum of the input
and 5 in the Immediate window.
Exercise 2.5. Read the Application.OnKey Method (Excel) at https://
msdn.microsoft.com/en-us/library/office/ff197461.aspx and write
a sub procedure named OnKeyDemo that assigns the key combination “Shift
+ Ctrl + A” to the following sub procedure:
1 Sub HelloWorld ()
2 MsgBox " Hello World ! "
3 End Sub
1 Sub WorkbookDemo1 ()
2 Debug . Print Application . ThisWorkbook . FullName
3 Debug . Print Application . ActiveWorkbook . FullName
4 Debug . Print Application . Workbooks (1) . FullName
5 End Sub
Executing the above code produces the following output in the Immediate
window:
1 Scratch : gan : ResearchU : trunk : book : vbapfi : code : Chapter2
. xlsm
2 Scratch : gan : ResearchU : trunk : book : vbapfi : code : Chapter2
. xlsm
3 Scratch : gan : ResearchU : trunk : book : vbapfi : code : Chapter2
. xlsm
The above output was produced by running the code on a Mac® computer.
That is why we see colons in the file paths. If we run the code on a Windows®
machine, we see the following output:
1 C :\ Users \ Guojun Gan \ Documents \ ResearchU \ trunk \ book \
vbapfi \ code \ Chapter2 . xlsm
2 C :\ Users \ Guojun Gan \ Documents \ ResearchU \ trunk \ book \
vbapfi \ code \ Chapter2 . xlsm
3 C :\ Users \ Guojun Gan \ Documents \ ResearchU \ trunk \ book \
vbapfi \ code \ Chapter2 . xlsm
From the output, we see that all three approaches refer to the same work-
book.
There are several types of Excel workbooks, each of which has a differ-
ent file extension. Table 2.3 presents a list of Excel file extensions. Except
for the extension .xll, all file extensions were introduced with Excel 2007.
Using these extensions, we can save a workbook without macros or with
macros. We can also save a workbook as a template without macros or with
macros. Add-in workbooks are workbooks that contain code. Using add-in
workbooks allows the user to separate code from the users’ data.
Extension Description
.xlsx Workbook
.xlsm Macro-enabled workbook; same as .xlsx but may contain
macros and scripts
.xltx Template
.xltm Macro-enabled template; same as .xltx but may contain
macros and scripts
.xlam Add-in workbook
Property Description
ActiveChart Returns a Chart object that represents the active chart
ActiveSheet Returns an object that represents the active sheet
Charts Returns a Sheets collection representing all
the chart sheets
FullName Returns the name of the object, including its path
IsAddin True if the workbook is running as an add-in
Name Returns the name of the object
Path Returns a string representing the complete path
to the workbook
Sheets Returns a Sheets collection that represents
all the sheets in the workbook
Worksheets Returns a Sheets collection that represents all the
worksheets in the specified workbook
A Workbook object has many properties. Table 2.4 shows a list of some
commonly used properties of a Workbook object. We already saw how to
26 Excel Objects
The output shows that the current workbook contains one worksheet and
no charts. The workbook is not an add-in workbook.
Method Description
Activate Activates the first window associated with the workbook
Close Closes the object
Save Saves changes to the specified workbook
SaveAs Saves changes to the workbook in a different file
Note that a Workbook object does not have the Open method.
Property Description
Count Returns the number of objects in the collection
Item Returns a single object from the collection
Method Description
Add Creates a new workbook, which becomes
the active workbook
Close Closes all workbooks
Open Opens a workbook
The Workbooks object does not have many properties or methods. Table
2.6 and Table 2.7 present some properties and methods of the Workbooks
object. From Table 2.7, we see that the Workbooks object provides methods
to create and open a workbook. For example, we can open the workbook
"Chapter2Copy.xlsm" (See Exercise 2.7) as follows:
1 Sub WorkbookDemo5 ()
2 Debug . Print ActiveWorkbook . Name
3 Workbooks . Open " Chapter2Copy . xlsm "
4 Debug . Print Workbooks . Count
5 Debug . Print ActiveWorkbook . Name
6 End Sub
From the output we see that the active workbook was changed to the work-
book opened by the Open method.
28 Excel Objects
Property Description
Cells Returns a Range object that represents all the cells on
the worksheet
Columns Returns a Range object that represents all the columns on
the active worksheet
Index Returns the index number of the object within
the collection of similar objects
Name Returns or sets a String value that represents
the object name
Next Returns a Worksheet object that represents the next sheet
Range Returns a Range object that represents a cell or
a Range of cells
Rows Returns a Range object that represents all the rows on
the specified worksheet
Type Returns an XlSheetType value that represents
the worksheet type
UsedRange Returns a Range object that represents the used range on
the specified worksheet
7 End Sub
The above code displays the index, name, and used range of the active sheet.
Then the code changes the name of the active sheet and displays the name
again. Executing the code produces the following output:
1 1
2 SheetA
3 $A$1 : $G$110
4 SheetA
From the output, we see that the used range is “A1:G110.” This means that
all the cells outside of “A1:G110” are not used. It is possible that some cells
in the used range are not used.
The following sub procedure shows how to copy and select a worksheet:
1 Sub WorksheetDemo3 ()
2 Debug . Print ActiveSheet . Name
3 ActiveSheet . Copy After := ActiveSheet
4 Debug . Print ActiveSheet . Name
5 ActiveSheet . Name = " Sheet2 "
6 Debug . Print ActiveSheet . Name
7 Sheets ( " SheetA " ) . Select
8 Debug . Print ActiveSheet . Name
9 End Sub
Executing the above code produces the following output in the Immediate
window:
1 SheetA
2 SheetA (2)
30 Excel Objects
Method Description
Activate Makes the current sheet the active sheet
Calculate Calculates all open workbooks, a specific worksheet,
or a specified range on a worksheet
Copy Copies the sheet to another location in the workbook
Delete Deletes a worksheet
Move Moves the sheet to another location in the workbook
Paste Pastes the contents of the Clipboard onto the sheet
PasteSpecial Pastes the contents of the Clipboard onto the sheet,
using a specified format
SaveAs Saves changes to the chart or worksheet
in a different file
Select Selects the object
3 Sheet2
4 SheetA
From the output, we see that the new worksheet becomes the active sheet.
The following sub procedure shows how to delete a worksheet:
1 Sub WorksheetDemo4 ()
2 Debug . Print ActiveSheet . Name
3 ActiveSheet . Copy After := ActiveSheet
4 Debug . Print ActiveSheet . Name
5 ActiveSheet . Delete
6 Debug . Print ActiveSheet . Name
7 End Sub
If you run the above code, you will see a prompt dialog. If you click the
“Delete” button in the dialog, you will see the following output in the Imme-
diate window:
1 SheetA
2 SheetA (2)
3 SheetA
From the output, we see that the active sheet changed to the previous
worksheet after the active sheet was deleted. If you do not want to see
the prompt dialog when deleting a worksheet, you can set the application
Worksheet Objects 31
Executing the above code will not show the prompt dialog.
Method Description
Add Creates a new worksheet, chart, or macro sheet, which
becomes the active sheet
Copy Copies the sheet to another location in the workbook
Delete Deletes the object
Move Moves the sheet to another location in the workbook
h
32 Excel Objects
cells.
We can refer to a Range object in different ways. Table 2.11 gives a list of
expressions that can be used to refer to a Range object. In this table, we only
show the expression to refer to a Range object in the ActiveWorksheet
object. To refer to a Range object in a worksheet that is different from the
active worksheet, we can include the parent object in the reference.
Expression Description
Range("A3:C4") Cells in Columns A to C and
Rows 3 to 4
Range("A1") The cell in Column A and Row 1
Range("RName") The range named "RName"
Range("1:1") Cells in Row 1
Range("A:A") Cells in Column A
Range("A1:C3,H5:K6") Cells in noncontiguous ranges
Cells(1,1) Equivalent to Range("A1")
Range(Cells(3,1), Cells(4,3)) Equivalent to Range("A3:C4")
Range("A1").Offset(2,3) The cell two rows below A1 and
three columns to the right of A1
1 Sub MaleTable ()
2 Debug . Print Workbooks ( " USLifeTable1999 -2001 Male .
xls " ) . Worksheets ( " Sheet1 " ) . Range ( " A25 : B134 " ) .
Cells . Count
3 End Sub
In the above code, we refer to the workbook and the worksheet that contain
the Range object. Executing the sub procedure gives the number of cells
contained in the range.
From Table 2.11, we see that we can also refer to a Range object by Cells.
As pointed out by Walkenbach (2013b), Cells is a property not an object
or collection. VBA evaluates a Cells property and returns a Range object.
The Cells property takes two arguments. The first argument represents
the row index, and the second argument represents the column index. The
Cells property provides a useful way to refer to a range if we know the
coordinates of the range. Suppose that we want to find the address of the
range in the intersection of Rows 10–12 and Columns 20–22. Then we can
use the following sub procedure:
1 Sub FindAddress ()
2 Debug . Print Range ( Cells (10 , 20) , Cells (12 , 22) ) .
Address
34 Excel Objects
3 End Sub
The Offset provides another useful way to refer to a Range object rela-
tive to another Range object. Like the Cells object, the Offset object also
takes two arguments, which represent the number of rows and the number
of columns to offset, respectively. For example, we can refer to the cell “B2”
as follows:
1 Sub ReferCell2 ()
2 Debug . Print Range ( " A1 " ) . Offset (1 , 1) . Address
3 Debug . Print Range ( " E4 " ) . Offset ( -2 , -3) . Address
4 End Sub |
Calculate the output before running the code in the Visual Basic Editor.
Range Object 35
A Range object has many properties. Table 2.12 presents a list of some
commonly used properties of a Range object. Some of these properties are
read-only. For read-only properties, VBA code can look at their values but
cannot change them. Examples of read-only properties include Text and
Count. If a property is not read-only, then you can get and set the value of
the property.
The following example shows how to set the value of a cell, change the
number format, and display the string as shown in the worksheet:
1 Sub ShowValueText ()
2 Range ( " A1 " ) . Value = 198.35
3 Range ( " A1 " ) . NumberFormat = " $ # ,##0 _ ) ;( $ # ,##0) "
4 Debug . Print Range ( " A1 " ) . Value
5 Debug . Print Range ( " A1 " ) . Text
6 End Sub
g
36 Excel Objects
If we execute the above sub procedure, cell “C3” will show the word “White”
in white and the background color will be changed to blue. Note that vbBlue
and vbWhite are VBA constants used to represent colors.
Method Description
Select Select a range of cells
Copy Copy a range
PasteSpecial Paste a range from the Clipboard into a specified
range
Clear Delete contents of a range
Delete Delete a range and fill it up by shifting remaining cells
A Range object also has many methods. Table 2.13 shows some com-
monly used methods of a Range object. The following piece of code shows
how to copy and paste a range:
1 Sub CopyRange ()
2 Range ( " A1 : C3 " ) . Copy
3 Range ( " D5 " ) . PasteSpecial xlPasteAll
4 Debug . Print Selection . Address
5 End Sub |
After executing the above code, the range “A1:C3” will be copied to the
range “D5:F7.” Since we specify xlPasteAll, the format of the range is also
copied.
WorksheetFunction Object 37
Note that a Range object does not have a method called paste. In fact,
paste is a method of a Worksheet object. For example, we can copy a range
as follows:
1 Sub CopyRange2 ()
2 Range ( " A1 : C3 " ) . Copy
3 Range ( " D5 " ) . Select
4 ActiveSheet . Paste
5 Debug . Print Selection . Address
6 End Sub
The sub procedure CopyRange and the sub procedure CopyRange2 produce
the same result. If we just want to copy values of a range, then we need to use
the PasteSpecial method by specifying xlPasteValues in the argument.
Function Description
Ceiling Returns number rounded up to the nearest multiple
of significance
Count Counts the number of cells that contain numbers and
counts numbers within the list of arguments
CountIf Counts the number of cells within a range that meet
the given criteria
Floor Rounds number down to the nearest multiple of
significance
Ln Returns the natural logarithm of a number
Lookup Returns a value either from a one-row or one-column
range or from an array
Max Returns the largest value in a set of values
Min Returns the smallest number in a set of values
NormDist Returns the normal distribution for the specified mean
and standard deviation
NormInv Returns the inverse of the normal cumulative
distribution for the specified mean and
standard deviation
Pi Returns the number 3.14159265358979
Power Returns the result of a number raised to a power
RandBetween Returns a random integer number between
two specified numbers
The above sub procedure first generates five random numbers and puts
them into the first five cells of Column A. Then it displays the number
of values that are larger than 0.5, the maximum of the five values, and
the minimum of the five values. Executing the above code produces the
following output:
1 3
2 1
3 0
If you execute the above code, you may get different results, as the func-
tion RandBetween returns a random integer between two numbers you
specified.
(x − µ)2
µ ¶
1
f (x, µ, σ) = p exp − . (2.1)
2πσ σ2
Note that a standard normal variable has a mean of 0 and a standard devia-
tion of 1.
2.7 Summary
In this chapter, we introduced the Excel object model, which contains
hundreds of objects and thousands of properties and methods. We also in-
troduced some commonly encountered objects, such as the Application,
Range, and Cells objects. For an overall view of the Excel object model,
readers are referred to (Roman, 2002, Chapter 15). For more information
about object-oriented programming, readers are referred to (Urtis, 2015,
Lesson 5).
3
Variables, Data Types, and Scopes
• know the common data types supported by Visual Basic for Applications
(VBA).
• know how to declare and use variables and constants.
• know how to declare and use arrays.
• know how to manipulate dates and strings.
• understand the concept of scopes.
Exercise 3.1. Determine the legal variable names in the following list of
names:
43
44 Variables, Data Types, and Scopes
Table 3.1 shows a list of VBA’s built-in data types. Regarding how to
choose a data type for a variable, you should in general choose a data type
that can hold all the data you want to store and uses the smallest number of
bytes. Most data types shown in Table 3.1 are easy to understand except for
Object and Variant.
We declare an Object variable using the Dim statement as above. How-
ever, we need to use the Set statement to assign an object to an object
variable. The following sub procedure shows how to declare Object vari-
ables and assign objects to the variables:
1 Sub VariableDemo3 ()
2 Dim wb As Object
3 Dim ws As Object
4
5 Set wb = ThisWorkbook
6 Debug . Print wb . Name
7
8 Set ws = ThisWorkbook . ActiveSheet
9 Debug . Print ws . Name
10 End Sub
Variable Declaration and Data Types 45
In the above code, we declare two Object variables and assign a Workbook
object and a Sheet object to the variables, respectively. Executing the above
code produces the following output:
1 Chapter1 . xlsm
2 Sheet1
2 Dim wb As Workbook
3
4 Set wb = ThisWorkbook . ActiveSheet
5 Debug . Print wb . Name
6 End Sub
1 1
2 3.54
3 Chapter1 . xlsm
From the output, we see that a Variant variable can be used to store differ-
ent types of data without modifying the data.
VBA is not a strictly typed programming language, that is, we can use a
variable without explicitly declaring it. In the following example, we assign
values to two variables and use the function VarType to get the data type of
the variables.
1 Sub VariableDemo2 ()
2 x = 1
3 Debug . Print VarType ( x )
4 Debug . Print vbInteger
5
6 y = 3.14
7 Debug . Print VarType ( y )
8 Debug . Print vbDouble
9 End Sub
3.2 Arrays
In Section 3.1, we introduced how to declare variables, which contain a
single value. In this section, we introduce how to declare arrays, which can
contain multiple values. In particular, we will introduce one-dimensional,
multidimensional, and dynamic arrays.
An array is a group of variables that share a common name. We can
refer to a specific variable in an array by using the array name and an index
number in parentheses. The following example shows how to declare one-
dimensional arrays:
1 Sub ArrayDemo1 ()
2 Dim arA (0 To 9) As Integer
3 Dim arB (9) As Integer
4 Dim arC (1 To 10) As Integer
5
6 Debug . Print UBound ( arA )
7 Debug . Print LBound ( arA )
8 Debug . Print UBound ( arB )
9 Debug . Print LBound ( arB )
10 Debug . Print UBound ( arC )
Arrays 49
In the above sub procedure, we declare three arrays, each of which has ten
elements. Inside the parentheses of arA, 0 means the lower index and 9
means the upper index of the array. The functions UBound and LBound are
used to get the upper and lower bounds of the array index. If we execute the
above code, we see the following output:
1 9
2 0
3 9
4 0
5 10
6 1
From the above output we see that arA and arB are identical in that the
bounds of the two arrays are the same. However, the bounds of arC are 1
and 10.
What are the bounds of the array arD, and how many elements does the
array contain?
From the above example we see that if you omit the lower index in
an array declaration, VBA assumes that it is 0. However, you can change
the default lower index to 1 by putting Option Base 1 in the declaration
section of the module. Suppose that the underlying module is MArray, and
we have Option Base 1 in the declaration section of the module. Then
executing the following code
1 Sub ArrayDemo3 ()
2 Dim arE (5) As Integer
3
4 Debug . Print LBound ( arE )
50 Variables, Data Types, and Scopes
Once we declare an array, we can access its elements by using the array
name and an index number in parentheses. The following example shows
how to get and set values of an array:
1 Sub ArrayDemo5 ()
2 Dim arA (1 To 5) As Double
3 arA (1) = 1.1
4 arA (3) = arA (1) + 2
5 arA (5) = arA (3) + 2
6
7 Debug . Print arA (1) , arA (2) , arA (3) , arA (4) , arA
(5)
8 End Sub
We can also use the functions LBound and UBound to get the bounds of
a multidimensional array. However, we need to specify which dimension in
the function calls. The following example shows how to get the bounds of a
multidimensional array:
1 Sub ArrayDemo8 ()
2 Dim arM (1 To 10 , 0 To 9 , 1 To 10) As Double
52 Variables, Data Types, and Scopes
3
4 Debug . Print LBound ( arM , 1) , LBound ( arM , 2) ,
LBound ( arM , 3)
5 Debug . Print UBound ( arM , 1) , UBound ( arM , 2) ,
UBound ( arM , 3)
6 End Sub
In the above code, we use the ReDim statement to change the size of the
array. We can use the ReDim statement as many times as we want. Executing
the above code gives the following output:
1 1 2 3
2 0 0 0 0 0
From the output, we see that the second ReDim statement destroyed all the
old values contained in the array arV.
To keep the old values while using the ReDim statement, we need to
use the Preserve statement after the ReDim statement, as shown in the
following example:
1 Sub ArrayDemo10 ()
2 Dim arV () As Integer
Arrays 53
3
4 ReDim arV (1 To 3)
5 arV (1) = 1
6 arV (2) = 2
7 arV (3) = 3
8 Debug . Print arV (1) , arV (2) , arV (3)
9
10 ReDim Preserve arV (1 To 5)
11 Debug . Print arV (1) , arV (2) , arV (3) , arV (4) , arV
(5)
12 End Sub
From the output, we see that the old values are preserved after changing
the size of the array.
h
54 Variables, Data Types, and Scopes
3.3 Constants
A variable’s value often changes during execution of a program. If we want
to refer to a value or string that never changes during execution of the
program, we should use a constant. A constant is a meaningful name that
takes the place of a number or string that does not change during execution
of a program.
The following example illustrates how to define constants:
1 Sub ConstantDemo1 ()
2 Const conPi As Double = 3.1415
3 Const conDaysInWeek As Integer = 7
4 Const conVersion As String = " v1 .0 "
5
6 Debug . Print conPi
7 Debug . Print conDaysInWeek
8 Debug . Print conVersion
9 End Sub
In the above sub procedure, we define three constants using the Const
statement: a real constant, an integer constant, and a string constant. Ex-
ecuting the above code produces the following output in the Immediate
window:
1 3.1415
2 7
3 v1 .0
In the above example, we defined the constant conPi2 based on the con-
stant conPi.
1 Option Explicit
2
3 Const conPi As Double = 3.1415
4 Const conDaysInWeek As Integer = 7
5 Const conVersion As String = " v1 .0 "
Then we can use these constants in all procedures in the module. In Section
3.6, we will discuss more about the scope of a constant.
We have discussed how to define our own constants. VBA also contains
many built-in constants. For example, Figure 3.2 shows a list of VBA’s built-
in color constants, and Figure 3.3 shows a list of VBA’s built-in constants for
variable types.
h
Strings 57
3.4 Strings
In many situations, we need to handle string (or text) data. VBA provides
a useful set of functions for handling strings. In this section, we introduce
how to create strings and use existing functions to manipulate them.
There are two types of strings in VBA: fixed-length strings and dynamic
strings. The following example shows how to declare a fixed-length string:
1 Sub StringDemo1 ()
2 Dim strFixed As String * 15
3
4 Debug . Print Len ( strFixed )
5 Debug . Print strFixed
6 strFixed = " I am a fixed string "
7 Debug . Print Len ( strFixed )
8 Debug . Print strFixed
9 End Sub
From the above output, we see that the function Len always returns the
fixed size of the string.
Unlike fixed-length strings, dynamic strings do not have a fixed size.
Creating a dynamic string is straightforward, as shown in the following
example:
1 Sub StringDemo2 ()
2 Dim strDynamic As String
3
4 Debug . Print Len ( strDynamic )
5 Debug . Print strDynamic
6 strDynamic = " I am a fixed string "
7 Debug . Print Len ( strDynamic )
8 Debug . Print strDynamic
9 End Sub
When we add or remove characters from a dynamic string, VBA will take
care of allocating or deallocating memory as necessary to store the text data.
If we execute the above code, we see the following output:
1 0
2
3 19
4 I am a fixed string
From the output, we see that the function Len returns the actual number of
characters contained in the string.
A fixed-length string cannot contain more than 65,526 characters. A
dynamic string can contain as many as two billion characters. Dynamic
strings are a bit slower to use because they require more processing effort
from VBA. However, we need to use trim functions to remove extra spaces
of a fixed-length string every time we use the string. As a result, we will use
fixed-length strings only when it is necessary.
Now let us introduce VBA’s built-in functions for manipulating strings.
Strings 59
Function Description
Filter Returns a zero-based array containing a subset of
a String array based on specified filter criteria
Format Returns a string formatted according to a specified format
InStr Returns the start position of the first occurrence of
one string within another
InStrRev Returns the position of the first occurrence of one string
within another, starting from the right side of the string
Join Returns a string created by joining substrings contained
in an array
LCase Returns a string or character converted to lowercase
Left Returns a string containing a specified number of
characters from the left side of a string
Len Returns the number of characters in a string
LSet Returns a left-aligned string containing
the specified string adjusted to the specified length
LTrim Returns a string containing a copy of a specified string
with no leading spaces
Mid Returns a string containing a specified number of
characters from a string
Replace Returns a string where a specified substring has been
replaced with another substring for a specified
number of times
Right Returns a string containing a specified number of
characters from the right side of a string
RSet Returns a right-aligned string containing
the specified string adjusted to the specified length
RTrim Returns a string containing a copy of a specified string
with no trailing spaces
Space Returns a string consisting of the specified number of
spaces
Split Returns a zero-based, one-dimensional array of substrings
StrComp Returns −1, 0, or 1 based on the result of
a string comparison
Trim Returns a string containing a copy of a specified string
with no leading or trailing spaces
UCase Returns a string or character containing the specified
string converted to uppercase
60 Variables, Data Types, and Scopes
Table 3.2 gives a list of commonly used string functions provided by VBA.
We first introduce how to compare two strings.
In Excel VBA, we have two methods of comparing strings: the binary
mode and the text mode. In the binary mode, strings are compared based
on their binary representation. In the binary mode, “A” and “a” are different.
In the text mode, strings are treated case insensitively. In this mode, “A” is
the same as “a.” We can specify the method of comparison in a module
by using the Option Compare statement in the declaration section. For
example, if we want to compare strings in the text mode in a module, we
can put the following line
1 Option Compare Text
Suppose that VBA uses the default comparison mode (i.e., the binary mode)
in the module where the above code is saved. Then, executing the above
code gives the following output:
1 True
2 False
3 False
4 -1
5 0
The above output shows that strA is less than strB in the binary mode.
However, the two strings are the same if compared in the text mode. In the
StrComp function, we can specify the mode of comparison.
Strings 61
The output shows that leading and trailing spaces in a string affect the result
of string comparison. We use the Trim function to remove the leading and
trailing spaces in the string as follows:
1 Sub StringDemo5 ()
2 Debug . Print StrComp ( " Hello " , Trim ( " Hello " ) ,
vbTextCompare )
3 Debug . Print StrComp ( Trim ( " Hello " ) , " Hello " ,
vbTextCompare )
4 End Sub
The output shows that the two strings are the same when the spaces are
removed.
Exercise 3.16. The following function returns an array of 1000 words gen-
erated randomly:
1 Function RandomWords ()
2 Dim arV (1 To 1000) As String
3 Dim i As Integer
4 Dim j As Integer
5 Dim k As Integer
6 Dim strW As String
7
62 Variables, Data Types, and Scopes
8 Rnd ( -1)
9 Randomize (1)
10 For i = 1 To 1000
11 strW = " "
12 For j = 1 To 4
13 k = Math . Round ( Rnd * ( Asc ( " e " ) - Asc ( " a " )
+ 1) , 0) + Asc ( " a " )
14 strW = strW & Chr ( k )
15 Next j
16 arV ( i ) = strW
17 Next i
18
19 RandomWords = arV
20 End Function
The function InStr can take four arguments. The first and the last argu-
ments are optional. The default value of the first argument is 1, and the
default value of the last argument is vbBinaryCompare. Executing the above
code gives the following output:
Strings 63
1 6
2 6
3 0
4 6
5 0
In the above example, we use the function Replace to replace the substring
“car” with “house.” Executing the above code gives the following output:
1 This house is beautiful
Exercise 3.17. The function Replace has six arguments. Read the Microsoft
Developer Network (MSDN) document of this function and write a sub
procedure named StringDemo9 to replace only the first occurrence of “car”
in the following string with “house”:
To extract a substring from a string, we can use the Mid function. The
following example shows how to extract a substring from a string:
1 Sub StringDemo10 ()
2 Dim strA As String
3 Dim strB As String
4 Dim intA As Integer
5 strA = " This car is beautiful . That car is big . "
64 Variables, Data Types, and Scopes
6
7 intA = InStr ( strA , " . " )
8 strB = Mid ( strA , 1 , intA )
9
10 Debug . Print strB
11 End Sub
In the above example, we find the first dot in the string and then use the
Mid function to extract the substring. The Mid function can take three ar-
guments: the first argument is a string, the second one is the start position,
and the third one is the length of the substring to be extracted. Executing
the above code produces the following output:
1 This car is beautiful .
Display the lengths of the original string and the resulting string to make
sure the difference of the lengths is 2. (The word “house” has two more
letters than “car.”)
“<td>name</td><td>procedure</td><td>word</td>”
To split a string with a delimiter, we can use the Split function. The
following example shows how to split a string:
1 Sub StringDemo13 ()
2 Dim strA As String
3 Dim arB () As String
4
5 strA = " Apple and Banana "
6 arB = Split ( strA , " and " )
Dates 65
7
8 Debug . Print LBound ( arB ) , UBound ( arB )
9 Debug . Print arB (0) & " -" & arB (1)
10 End Sub
In the above code, the Split function takes two arguments: the first ar-
gument is the string to be split, and the second argument is the delimiter.
Executing the above code produces the following output:
1 0 1
2 Apple - Banana
"1.1,0.2,3.14,4.2,0.5,4.8,1.3,6.2,1"
that consists of numbers separated by commas.
3.5 Dates
In many VBA programs, we need to deal with dates at one point or another.
VBA provides some built-in functions for creating and manipulating date
and time. Table 3.3 shows a list of VBA’s built-in functions for manipulating
date and time. In this section, we introduce how to use these function to
manipulate date and time.
The following example shows how to get the current date and time and
extract a portion from a time value:
1 Sub DateDemo1 ()
2 Debug . Print Now
3 Debug . Print Date
4 Debug . Print Year ( Now )
5 Debug . Print Month ( Now )
6 Debug . Print Day ( Now )
7 Debug . Print Hour ( Now )
66 Variables, Data Types, and Scopes
Function Description
Date Returns the current date
DateAdd Add and subtract dates
DateDiff Returns the difference in dates
DatePart Returns a portion of a date
DateSerial Returns a Date value representing a specified year,
month, and day, with the time set to midnight
DateValue Returns a Date value containing the date information
represented by a string, with the time set to midnight
Day Returns an Integer value from 1 through 31
representing the day of the month
Format Formats a date according to a specified format
Hour Returns the hour (0 to 23) of a time value
Minute Returns the minute (0 to 59) of a time value
Month Returns the month (1 to 12) of a date value
MonthName Returns a string representing the month given
a number from 1 to 12
Now Returns the current date and time
WeekDay Returns a number representing the day of the week
WeekDayName Returns a string representing the day of the week given
a number from 1 to 7
Year Returns a four-digit year (1900 to 9999) of a date value
10 23
The above example shows how to get the current date. We can also
create a date variable and assign a value to it. The following sub procedure
shows how to create a date value:
1 Sub DateDemo2 ()
2 Dim datA As Date
3
4 datA = #12/1/2005#
5 Debug . Print datA
6 Debug . Print Format ( datA , " yyyy / mm / dd " )
7 Debug . Print Format ( datA , " yy / m / d " )
8 End Sub
In the above example, we assign the value “12/1/2005” to the variable datA.
We notice that the date value “12/1/2005” is surrounded by the # sign.
We also use the Format function to display the date in specified formats.
Executing the above code gives the following output:
1 12/1/2005
2 2005/12/01
3 05/12/1
Exercise 3.21. Write a sub procedure named DateDemo3 to get the year,
month, and day of the following date value:
To find the number of time intervals between two Date values, we use
the DateDiff function. The following example shows how to use this func-
tion:
1 Sub DateDemo5 ()
2 Dim datA As Date
3 Dim datB As Date
4
5 datA = #3/20/2005#
6 datB = #1/22/2016#
7
8 Debug . Print DateDiff ( " d " , datA , datB ) ' days
between
9 Debug . Print DateDiff ( " ww " , datA , datB ) ' calendar
weeks between
10 Debug . Print DateDiff ( " w " , datA , datB ) ' weeks
between
11 Debug . Print DateDiff ( " yyyy " , datA , datB ) ' years
between
12 End Sub
of dates starting from January 1, 2016, to May 31, 2016, at an interval of one
week.
Exercise 3.23. Write a function procedure named MLK with one Integer-
type argument, such that MLK(y) returns a Date value representing the
Martin Luther King Day (the third Monday of January) of year y. What is the
output of the following code?
1 Sub DateDemo7 ()
2 Dim i As Integer
3
4 For i = 2010 To 2020
5 Debug . Print MLK ( i )
6 Next i
7 End Sub
Exercise 3.24. Write a function procedure named IsMLK with one Date-
type argument, such that IsMLK(dat) returns True if dat is a Martin Luther
King day, and False if dat is not a Martin Luther King day. What is the
output of the following code?
1 Sub DateDemo8 ()
2 Debug . Print IsMLK ( DateSerial (2016 , 1 , 18) )
3 Debug . Print IsMLK ( DateSerial (2015 , 1 , 19) )
4 Debug . Print IsMLK ( DateSerial (2010 , 1 , 20) )
5 End Sub
3.6 Scopes
In this section, we introduce the scopes of variables, constants, and proce-
dures.
A scope of a variable determines which modules and procedures can
use the variable. The scope of a variable is determined at the time when the
variable is declared. In VBA, there are three scopes available for variables:
procedure-only, module-only, and public. A procedure-only variable can
only be used in the procedure where the variable is declared. A module-only
70 Variables, Data Types, and Scopes
Scope Declaration
Procedure-only Use a Dim or Static statement in the procedure
Module-only Use a Dim or Private statement before the
first procedure in the module
Public Use a Public statement before the first procedure
in a module
Table 3.4 lists approaches to declare a variable that has different scopes.
In the following sub procedure, we declare two procedure-only variables:
1 Sub ScopeDemo1 ()
2 Dim intA As Integer
3 Static intCount As Integer
4
5 intA = intA + 1
6 intCount = intCount + 1
7
8 Debug . Print intA
9 Debug . Print intCount
10 End Sub
The variables intA and intCount can only be used in the procedure
ScopeDemo1. The difference between the two variables is that one is de-
clared with the Dim statement we used before, and the other is declared
with the Static statement. The variable intA will be reset every time when
the procedure is called. However, the variable intCount remains in exis-
tence the entire time VBA is running. A static variable is reset when any of
the following occur:
Now, let us introduce how to declare public variables that are available
to all procedures in all modules. Suppose that a module named MScope3
contains the following content:
1 ' Module MScope3
2
3 Public strMainFile As String
4
5 Sub ScopeDemo4 ()
6 Debug . Print strMainFile
7 End Sub
72 Variables, Data Types, and Scopes
Then we can use the variable strMainFile in other modules. For example,
we can write a sub procedure in the module MScope1 as follows:
1 Sub scopedemo5 ()
2 ScopeDemo3 . strMainFile = " Chapter1 . xlsm "
3 End Sub
Scope Declaration
Procedure-only Use a Const statement in the procedure
Module-only Use a Const or Private Const statement before the
first procedure in the module
Public Use a Public Const statement before the first
procedure in a module
Exercise 3.25. Let MS1 be the name of a module containing the following
content:
1 ' MS1
2 Public Const conPi As Doulbe = conPi2 / 2
3
4 Sub PrintPi ()
5 Debug . Print conPi
6 End Sub
4 Sub PrintPi ()
5 Debug . Print conPi2
6 End Sub
Scope Declaration
Module-only Use a Private Sub to define the procedure
Public Use a Sub or Public Sub statement to define the
procedure
3.7 Summary
In this chapter, we introduced how to declare variables, arrays, and con-
stants in VBA. A variable is a named storage location in a computer’s mem-
ory. An array is a group of variables that share a common name. A constant is
a meaningful name that takes the place of a number or string that does not
change during execution of a program. We also introduced how to manipu-
late strings and dates. Finally, we gave a brief introduction to the concept
of scopes for variables, constants, and procedures. For more information
about manipulating strings and dates in VBA, readers are referred to Getz
and Gilbert (2001).
4
Operators and Control Structures
4.1 Operators
There are four types of operators in VBA: arithmetic operators, string opera-
tors, comparison operators and logical operators.
Table 4.1 gives a list of VBA’s arithmetic operators. The default prece-
dences of the operators are also listed and are applied in the absence of
brackets. All the operators are apparent to most people except the modulus
operator. The modulus operator finds the remainder after the division of
75
76 Operators and Control Structures
one number by another. The following sub procedure shows how to use
these operators:
1 Sub OperatorDemo1 ()
2 Debug . Print 1 + 2 * 3
3 Debug . Print (1 + 2) * 3
4 Debug . Print 2 ^ 3 * 3
5 Debug . Print 10 Mod 3
6 Debug . Print 8 - 4 / 2
7 Debug . Print (8 - 4) / 2
8 End Sub
(1 − 2)2 1
− − ln(2π) − ln 3.
2 × 32 2
Exercise 4.2. What is the output of the following code?
1 Sub OperatorDemo3 ()
2 Debug . Print 216 + 2 * 30 ^ 2
3 End Sub
If you multiply two large integers, VBA will raise an overflow error. For
example, if you execute the following code:
1 Sub OperatorDemo4 ()
2 Debug . Print 1000 * 1000
3 End Sub
Operators 77
you will see a run-time error “Overflow.” The reason is that VBA treats the
two numbers as integers. Since the product of the two integers is outside the
range of an integer in VBA, VBA raised the run-time error. To fix this problem,
we can convert the integer to double before doing the multiplication:
1 Sub OperatorDemo5 ()
2 Debug . Print 1000# * 1000
3 End Sub
The symbol # after 1000 means that 1000 is a double, not an integer. We can
also use the function CDbl to convert a number to a double:
1 Sub OperatorDemo6 ()
2 Debug . Print CDbl (1000) * 1000
3 End Sub
Operator Description
& Concatenation operator
+ Concatenation operator
Table 4.2 shows two string operators. VBA’s main string operator is the
concatenation operator &, which is used to concatenate two strings. The
following example shows how to use the & operator:
1 Sub OperatorDemo8 ()
2 Debug . Print " Abc " & " 123 "
3 Debug . Print " Abc " & CStr (123)
4 Debug . Print ThisWorkbook . Name & " is " & " " & " a
workbook "
5 End Sub
In the above code, we use the function CStr to convert the number 123 to a
string. Executing the above code produces the following code:
1 Abc123
2 Abc123
3 Chapter4 . xlsm is a workbook
The plus sign + can also be used to concatenate two strings. If we apply
+ to two strings, we get a concatenated string. If we apply + to two numbers,
we get the sum of the numbers. If we apply + to a number and a string,
we may get an error. The following example shows the application of + in
various situations:
1 Sub OperatorDemo9 ()
2 Debug . Print " abc " + " 123 "
3 Debug . Print " 123 " + 456
4 Debug . Print " 123 " + " 456 "
Operators 79
From the output, we see that the string "123" was converted to the number
123 automatically.
Operator Description
= Equality
<> Inequality
< Less than
> Greater than
<= Less than or equal to
>= Greater than or equal to
Is Compare two object reference variables
IsNot Compare two object reference variables
80 Operators and Control Structures
Operator Description
Not Logical opposite
And Logical conjunction
Or Logical disjunction or inclusion
Xor Logical exclusion
Table 4.3 and Table 4.4 give a list of comparison operators and a list of
logical operators, respectively. These operators are usually used together.
The following example shows how to use these operators:
1 Sub OperatorDemo12 ()
2 Debug . Print 1 = 2
3 Debug . Print Not 1 = 2
4 Debug . Print 1 <> 2
5 Debug . Print 1 < 2
6 Debug . Print 1 > 2
7 Debug . Print 1 <> 2 Or False
8 Debug . Print 1 <> 2 And False
9 Debug . Print True Xor False
10 Debug . Print True Xor True
11 Debug . Print False Xor False
12 End Sub
1 Sub OperatorDemo13 ()
2 Debug . Print (1 > 2 Or 2 = 2) And ( False Xor True )
3 Debug . Print ( Not 1 <> 2) Xor (2 = 3 And 3 = 3)
4 End Sub
Construct Description
GoTo statement Jump to a particular statement
If-Then statement Does something if a condition is true
Select Case statement Does one of several things according to
something’s value
In the above code, we defined a label named “LabelA” in Line 7. From the
code, we see that a label is just a string followed by a colon. Since we used a
GoTo statement in Line 4, the code in Line 5 is skipped. Executing the above
code produces the following output:
1 1
2 2
3 4
9 Debug . Print 4
10 GoTo LabelB
11 Debug . Print 5
12
13 End Sub
In the above code, we have three If-Then statements. The first If-Then
statement checks whether today is in February; the second statement
checks whether today is in the third week of the month; and the last state-
ment checks whether today is a Monday. The function Date returns today’s
date. We also use the VBA function Month, Day, and WeekDay to get the
month, day, and week day from today’s date, respectively.
Exercise 4.10. Labor Day is the first Monday in September. Write a sub
procedure named IfThenDemo4 to check whether today is Labor Day.
9 GoTo LabelA
10 End If
11 LabelB :
12 Debug . Print intTmp
13 End Sub
The above sub procedure displays “Today is in the first half of the month” if
the day is between 1 and 15 (inclusive), or displays “Today is in the second
half of the month” if the day is between 16 and 31 (inclusive). We can also
use a single number in a case, as shown in the following example:
1 Sub SelectDemo2 ()
2 Dim intWeekDay As Integer
3
4 intWeekDay = Weekday ( Date )
5
6 Select Case intWeekDay
7 Case 1
8 Debug . Print " Today is Sunday "
9 Case 2 To 6
10 Debug . Print " Today is a WeekDay "
11 Case 7
12 Debug . Print " Today is Saturday "
86 Operators and Control Structures
13 End Select
14 End Sub
In the above code, we use the function Format to convert a date into a
three-letter month symbol. Then we display the full name of the month.
Since there are twelve months, we did not write down all the cases. Instead,
we used the Case Else to include all other cases.
Exercise 4.12. The VBA function VarType returns the data type (e.g.,
vbDouble, vbCurrency, vbString, etc; see VarType in the Object Browser
for a complete list) of a variable. Write a sub procedure named SelectDemo4
to display the data type of the value of Cell “A1” in the active sheet.
4.3 Loops
In this section, we introduce VBA’s programming constructs that can be
used to execute a series of statements multiple times. These constructs are
referred to as loops.
Table 4.6 shows three types of loops supported by the VBA programming
language: the For-Next loop, the Do-While loop, and the Do-Until loop.
Loops 87
Construct Description
For-Next loop Executes some statements for a specified
number of times
Do-While loop Executes some statements as long as something
remains true
Do-Until loop Executes some statements until something
becomes true
Let us first introduce the For-Next loop, which can be used to execute a
series of statements for a specified number of times.
The following example shows how to use the For-Next loop:
1 Sub ForNextDemo1 ()
2 Dim dSum As Double
3 Dim n As Integer
4
5 For n = 1 To 100
6 dSum = dSum + n
7 Next n
8
9 Debug . Print dSum
10 End Sub
In the above code, we use a For-Next loop to calculate the sum of integers
from 1 to 100. Executing the code gives the following output:
1 5050
In a For-Next loop, we can also specify the step size by adding a Step
statement after the For statement. The following example shows how to
specify a step size:
1 Sub ForNextDemo4 ()
2 Dim vA (1 To 100) As Double
3 Dim n As Integer
4
5 For n = LBound ( vA ) To UBound ( vA ) Step 2
6 vA ( n ) = 1
7 Next n
8
9 Debug . Print vA (1) ; vA (2) ; vA (3) ; vA (4) ; vA (5)
10 End Sub
Note that the semicolon “;” after the Print function means printing imme-
diately after one another.
(0, 1, 0, 0, 1, 0, 0, 1, 0, . . . , 0, 1, 0),
(2, 1, 3, 2, 1, 3, 2, 1, 3, . . . , 2, 1, 3),
g
Exercise 4.18. Write a sub procedure named ForNextDemo9 to create the
following 100 × 100 array:
0 1 2 3 · · · 99
1 2 3 4 · · · 100
2 3 4 5 · · · 101 .
.. .. .. .. .. ..
. . . . . .
99 100 101 102 · · · 198
90 Operators and Control Structures
Then put the array into the range “A1:CV100” of the active worksheet.
N
i 2 > 1000,
X
i =1
12 Debug . Print n
13 Debug . Print dSum
14 End Sub
Exercise 4.21. The IsEmpty function can be used to check whether a cell is
empty or not. Write a sub procedure named ForNextDemo13 to display the
row index of the first empty cell in the first column of the active worksheet.
N
i 2 > 1000.
X
i =1
1 Sub DoUntilDemo1 ()
2 Dim dSum As Double
3 Dim n As Integer
4
5 Do Until dSum > 1000
6 n = n + 1
7 dSum = dSum + n ^ 2
8 Loop
9
10 Debug . Print n
11 Debug . Print dSum
12 End Sub
From the above example, we see that the Do-Until statement and the
Do-While statement are exchangeable by just changing the condition state-
ment.
4.4 Summary
In this chapter, we introduced VBA’s built-in operators and some control
structures. VBA provides four types of operators: arithmetic operators, string
operators, comparison operators, and logical operators. The control struc-
tures in VBA include If-Then, Select-Case, and GoTo. VBA loops include
For-Next, Do-While, and Do-Until.
5
Functions, Events, and File IO
• create functions.
• create event handlers.
• read and write text files.
Executing the above code produces the following output in the Immediate
window:
95
96 Functions, Events, and File IO
1 3
2 9
The function can also be used in cell formulas. For example, we can use the
function in Cell “C1” by typing “=MySum(A1,B1)” in Cell “C1.” Then, the
value of Cell “C1” will be the sum of the two numbers in Cells “A1” and “B1.”
Exercise 5.1. Write a function procedure named MySum2 with one Long-
type argument, such that MySum2(n) returns the following value:
n
i 2.
X
i =1
Exercise 5.2. Write a function procedure named DNorm with three Double-
type arguments, such that DNorm(x ,µ,σ) calculates the following value
(x − µ)2
µ ¶
1
f (x, µ, σ) = p exp − ,
2πσ 2σ2
where exp(·) is the exponential function, i.e., exp(y) = e y . What is the output
of the following code?
1 Sub FunctionDemo3 ()
2 Debug . Print DNorm (0 , 0 , 1)
3 Debug . Print DNorm (0 , 2 , 3)
4 Debug . Print Works heetFunc tion . NormDist (0 , 0 , 1 ,
False )
5 Debug . Print Works heetFunc tion . NormDist (0 , 2 , 3 ,
False )
6 End Sub
The above function calculates the sum of all elements of the input array. We
can test this function using the following sub procedure:
1 Sub FunctionDemo4 ()
2 Dim arA (1 To 100) As Double
3 Dim i As Integer
4
5 For i = 1 To 100
6 arA ( i ) = i
7 Next i
8
9 Debug . Print MySum3 ( arA )
10 End Sub
Exercise 5.3. The sample mean and the sample standard deviation of a vec-
tor of numbers can be calculated incrementally as follows. Let y 1 , y 2 , . . . , y n
be n numbers. Let {M 1,i : i = 1, 2, . . . , n} and {M 2,i : i = 1, 2, . . . , n} be two
sequences defined as follows:
y k − M 1,k−1
M 1,1 = y 1 , M 1,k = M 1,k−1 + , k = 2, 3, . . . , n,
k
98 Functions, Events, and File IO
and
k −1
M 2,1 = 0, M 2,k = M 2,k−1 + (y k − M 1,k−1 )2 , k = 2, 3, . . . , n.
k
Then,
ȳ = M 1,n
and s
M 2,n
sy = , n > 1,
n −1
where ȳ and s y denote the sample mean and the sample standard deviation
of y 1 , y 2 , . . . , y n .
Write two function procedures named IncMean and IncStd with one
array-type argument, such that IncMean(y) and IncStd(y) calculate the
sample mean and the sample standard deviation of the array y incremen-
tally, respectively. What is the output of the following code?
1 Sub FunctionDemo5 ()
2 Dim vA (1 To 100) As Double
3 Dim i As Integer
4 For i = 1 To 100
5 vA ( i ) = Rnd () * 10000
6 Next i
7
8 Debug . Print IncMean ( vA )
9 Debug . Print IncStd ( vA )
10 Debug . Print Works heetFunc tion . Average ( vA )
11 Debug . Print Works heetFunc tion . StDev ( vA )
12 End Sub
Exercise 5.4. Write a function procedure named Factorial with one Long-
type argument, such that Factorial(n) calculates the following value
n
Y
n! = 1 × 2 × · · · n = i.
i =1
Exercise 5.5. Recursive functions are usually not efficient. We can al-
most always substitute a loop for recursion. Write a nonrecursive function
procedure named Fibonacci2 with one Long-type argument, such that
100 Functions, Events, and File IO
In the above code, the function RandNumbers has one Integer-type argu-
ment and returns an array of random numbers. We use the VBA functions
Rnd and Randomize to generate random numbers. The function Randomize
is used to fix the seed of the random number generator so that we can repeat
the random numbers every time we call the function RandNumbers. To test
this function, we use the following sub procedure:
1 Sub FunctionDemo8 ()
2 Dim arA As Variant
3
4 arA = RandNumbers (10)
5 Debug . Print LBound ( arA ) , UBound ( arA )
6 Debug . Print arA (1) , arA (2) , arA (3)
7 End Sub
5.2 Events
A Visual Basic for Applications (VBA) sub procedure can be executed au-
tomatically when a particular event occurs. In this section, we introduce
some Excel® event types and where to place event-handler VBA code.
Table 5.1 gives a list of workbook events. To add a workbook event-
handler, we need to double click ThisWorkbook in the VBAProject window
102 Functions, Events, and File IO
Event Description
Activate The workbook is activated
BeforeClose The workbook is closed
BeforeSave The workbook is saved
Deactivate The workbook is deactivated
NewSheet A new sheet is added to the workbook
Open The workbook is opened
SheetActivate A sheet in the workbook is activated
SheetChange A cell in the workbook is changed
of the Visual Basic Editor, select Workbook in the left drop-down list of the
code window, and then select the event type in the right drop-down list of
the code window (see Figure 5.1). To add an event-handler to the NewSheet
event, for example, we select the NewSheet event from the drop-down list.
The following code is an example of the event-handler for the NewSheet
event:
1 Private Sub Workbook _NewShee t ( ByVal Sh As Object )
2 MsgBox " A new sheet named " & " " & Sh . Name & "
is added "
3 End Sub
Exercise 5.7. Write an event-handler for the NewSheet event, such that a
new sheet will be deleted without alert as soon as it is created.
Exercise 5.8. Add a piece of VBA code to a workbook, such that the total
number of sheets (including charts and worksheets) will not exceed 5.
Exercise 5.9. Write a piece of VBA code for your workbook, such that when-
ever a workbook is saved, a time stamp is written to the cell “D1” of the
sheet “Sheet1” of your workbook.
Event Description
Activate The worksheet is activated
Change A cell in the worksheet is changed
Deactivate The worksheet is deactivated
SelectionChange The selection is changed
Suppose that the cell “C3” of the worksheet “Sheet1” is a positive num-
ber that is an input for some program. We need to make sure the number
in the cell is positive. We can add an event-handler for the Change event.
To do that, we double click the worksheet named “Sheet1” and select the
Change event from the right drop-down list in the code window. Then, we
add the following code:
1 Private Sub Worksheet_Change ( ByVal Target As Range )
2 If Target . Parent . Name = " Sheet1 " And Target .
Address = " $C$3 " Then
3 If Target . Value <= 0 Then
4 MsgBox " Invalid number "
5 End If
6 End If
7 End Sub
The above code is executed when the name of the sheet is “Sheet1” and the
address of the changed cell is $C$3. If we input −1 in the cell C3 of the sheet
“Sheet1,” we will see a message box.
5.3 File IO
In this section, we introduce how to use VBA to read and write text files.
Let us introduce how to write text files using VBA code. Suppose that the
range “A1:B110” of the sheet “Sheet1” contains a mortality table. To write
the mortality table to a Comma Separated Values (CSV) file, we can use the
following VBA code:
1 Sub FileDemo1 ()
2 Dim strFileName As String
3 Dim ws As Worksheet
4 Dim i As Integer
File IO 105
5
6 Set ws = ThisWorkbook . Sheets ( " Sheet1 " )
7 strFileName = " MaleTable . csv "
8 Open strFileName For Output As #1
9 For i = 1 To 110
10 Write #1 , ws . Cells (i , 1) , ws . Cells (i , 2)
11 Next i
12 Close #1
13
14 End Sub
In the above code, we open a file for output in Line 8. The file object is
denoted by #1, as it is the first file we opened. In Lines 9–11, we write 110
lines to a CSV file named “MaleTable.csv.” In Line 12, we close the CSV file.
Executing the above code will produce a CSV file containing the numbers
in the range “A1:B110.”
If we open the file “MaleTable.csv” created by the above VBA code, we
see that all numbers are surrounded by double quotes. To write a CSV file
without double quotes, we can use the following VBA code:
1 Sub FileDemo2 ()
2 Dim strFileName As String
3 Dim ws As Worksheet
4 Dim i As Integer
5
6 Set ws = ThisWorkbook . Sheets ( " Sheet1 " )
7 strFileName = " MaleTable . csv "
8 Open strFileName For Output As #1
9 For i = 1 To 110
10 Print #1 , ws . Cells (i , 1) & " ," & ws . Cells (i ,
2)
11 Next i
12 Close #1
13
14 End Sub
In the above code, the semicolon “;” after the Print command tells the
command not to write a new line break.
writes a 100×10 matrix to the range “A1:J100.” Write a sub procedure named
FileDemo4 that writes the matrix to a CSV file named matrix.csv, such
that each line of the file contains a row of the matrix.
h
File IO 107
Now, let us introduce how to read text files in VBA. The following exam-
ple shows how to read a CSV file:
1 Sub FileDemo5 ()
2 Dim strFile As String
3 Dim strV () As String
4 Dim j As Integer
5 Dim n As Integer
6 strFile = ThisWorkbook . Path & " : matrix . csv "
7 ' strFile is a full file name with path in a Mac
computer
8
9 n = 1
10 Open strFile For Input As #1
11 Do Until EOF (1)
12 Line Input #1 , Line
13 strV = Split ( Line , " ," )
14 For j = 0 To UBound ( strV )
15 Cells (n , j + 1) . Value = strV ( j )
16 Next j
17 n = n + 1
18 Loop
19 Close #1
20 End Sub
Let us examine the above code line by line. In Lines 2–5, we declare variables.
In Line 6, we create the full file name by combining the path of the workbook
and “matrix.csv,” which is the intended file name. By doing that, we save
the file “matrix.csv” into the same directory as this workbook. In Line 10
of the above code, we open the file for input. In Lines 11–18, we use a loop
to read the CSV file line by line. The function EOF returns a boolean value
indicating whether the end of the file is reached. Inside the loop, we split the
line by commas and write the individual component to cells of the active
worksheet.
Exercise 5.12. Suppose that the file words.txt contains the following two
lines:
1 Microsoft Excel is an extremely powerful tool
2 You can use to manipulate and present data
from the file words.txt, write the words in the first line to the first row of
your active worksheet, and write the words in the second line to the second
row of your active worksheet. Each cell contains one word.
Exercise 5.13. The file keywords.html is an HTML file that contains VBA
keywords, which are enclosed by the strings “(v=vs.90).aspx">” and “<.”
5.4 Summary
In this chapter, we introduced how to create VBA functions, how to add a
handler to an event, and how to read and write files. A function allows us to
return some result to the caller of the function. An event handler enables
us to execute some VBA code automatically. The file Input-Output (IO)
functions allow us to import and export data from text files in a controlled
way. For more information about file IO, readers are referred to (Getz and
Gilbert, 2001, Chapter 12).
6
Error Handling and Debugging
109
110 Error Handling and Debugging
The above code asks the user for a number and then shows the square root
of the number in a message box. The above code is free of compilation
errors. However, it contains runtime errors because calculating the square
root of a negative number is illegal. For example, if we run the code and
input −1 in the input box, as shown in Figure 6.1(a), then we see the runtime
error shown in Figure 6.1(b).
(a)
(b)
The error message shown in Figure 6.1(b) is not helpful for many people.
A better way to handle the runtime error is to anticipate the error by adding
error-handling code. For example, we can add error-handling code to the
above sub procedure as follows:
1 Sub ErrorDemo2 ()
2 Dim dVal As Double
3
4 dVal = InputBox ( " Input a number " )
5 If dVal < 0 Then
6 MsgBox " You must input a non - negative number . "
7 Exit Sub
8 End If
9 MsgBox " The square root of " & dVal & " is " & Sqr
( dVal )
10 End Sub
(a)
(b)
In the above code, we change the type of the variable dVal to Variant. Then
we use the function IsNumeric to check whether the input is a number. If
it is not a number, we display a message and exit the sub procedure.
Does the above function contain runtime errors? If so, improve the function
by adding error-handling code.
Does the above code contain runtime errors? If so, what are the possible
runtime errors?
Statement Description
On Error GoTo label After executing this statement, VBA
resumes execution at the line with the label
On Error GoTo 0 After executing this statement, VBA
resumes its normal error-checking behavior
On Error Resume Next After executing this statement, VBA ignores
all errors and resumes execution with
the next statement
11 End Sub
The above code is able to trap all errors. If an error occurs, the code will
display the error number in a message box. Here we use the Err.Number to
get the error number. Note that we use the Exit Sub statement right before
the BadInput label. This statement is necessary because we do not want to
execute the error-handling code if no errors occur.
Statement Description
Resume After executing this statement, VBA resumes
with the statement that caused the error
Resume Next After executing this statement, VBA ignores all errors
and resumes execution with the next statement
Resume label After executing this statement, VBA resumes
execution at the line with the label
6 TryAgain :
7 On Error GoTo BadInput
8
9 dVal = InputBox ( " Input a number " )
10 MsgBox " The square root of " & dVal & " is " & Sqr
( dVal )
11
12 Exit Sub
13 BadInput :
14 strMsg = " Error number " & Err . Number & " occurred
."
15 strMsg = strMsg & vbNewLine & " Do you want to try
again ? "
16 intAns = MsgBox ( strMsg , vbYesNo + vbCritical )
17 If intAns = vbYes Then
18 Resume TryAgain
19 End If
20 End Sub
The above sub procedure calculates the square root of numbers in the
first column of the active worksheet and places the results into the second
column. Improve the sub procedure by adding error-handling code so that
the code can run through without showing error dialogs.
h
Debugging VBA Code 115
• Move the cursor to the statement where we want execution to stop and
then press “F9.”
• Move the cursor to the statement where we want execution to stop and
then click the “Debug/Toggle Breakpoint.”
• Click the gray margin to the left of the statement where we want execu-
tion to stop.
• Right-click the statement where we want execution to stop and click
“Toggle/Breakpoint” in the pop-up menu.
The above sub procedure first gets a file name of a workbook from a dialog
and then copies a range from the workbook to the active sheet of this
workbook.
Suppose that we set a breakpoint at Line 19 of the code. If we run
the code, VBA will enter Break model and the line with the breakpoint
is highlighted, as shown in Figure 6.3. In Break mode, we can type the
question mark "?" and a VBA statement to see the result. Figure 6.3 also
shows the results of four VBA statements. From the figure we see that the
value of the variable i is 1.
In some cases we want to see the values of variables near the end of a
loop. If the loop has 100 iterations and we want to see the values of variables
at the 90th iteration, then we need to respond to 90 prompts before the
Debugging VBA Code 117
code finally gets to the 90th iteration. A more efficient method to do this is
to set a watch expression.
Suppose that we want to examine the code at the 20th iteration of the
Fot-Next loop. We can set a watch expression that puts the procedure into
Break mode. Figure 6.4 shows how to set such a watch expression. If we
execute the code, VBA will enter into Break mode when the value of the
variable i is equal to 20. In Break mode, we can check the values of variables
in the Immediate window, as shown in Figure 6.5.
Another useful debugging tool is the Locals window, which can be used
to look at the values of local variables in Break mode. To view the Locals
window, we click the “View/Locals window.” Figure 6.6 shows the Locals
window in Break mode, which was triggered by the watch expression i=20.
In the Locals window, we see the value and type of the variables.
Figure 6.5: The Watches window and checking values of VBA statements in
the Immediate window in Break mode
Debugging VBA Code 119
2 Dim i As Integer
3 Dim dSum As Double
4 Dim dTemp As Double
5
6 Rnd ( -1)
7 Randomize (0)
8 For i = 1 To 1000
9 dTemp = Rnd ()
10 dSum = dSum + dTemp
11 Next i
12 End Sub
Do not modify the code. What is the value of the variable dTemp at the 900th
iteration?
There are three scopes, and the scope specifiers are given in Table 6.4. If
a variable is an array, the array specifier <array> is a. Otherwise, the array
specifier is omitted (see Table 6.5). The specifiers of various data types are
given in Table 6.6.
The following gives some examples of names based on the naming
conventions:
Best Practices of VBA Coding 121
Scope Specifier
Public g
Module-level m
Procedure-level nothing
Array Specifier
Array a
Not an array nothing
We will see more examples in later chapters when we introduce case studies.
The Business
Logic Tier
Finally let us introduce some best practice for general application devel-
opment in the following areas: code commenting, code readability, use of
module directives, use of variables and constants, defensive coding, version
control, and change documentation.
Good code commenting is an important practice in programming. In
VBA programming, there are three categories of comments: module-level
comments, procedure-level comments, and internal comments. A good
module-level comment should be placed at the top of the module and look
something like the following example:
1 '
2 ' Description : A brief description of the purpose of
3 ' the code in this module .
4 '
5 ' Revision history :
6 ' Date Developer Action
7 '
------------------------------------------------------
visual layout of code allows us to infer lots of information about the logic
structure of the program. Related code should be grouped together, and
unrelated code should be separated by blank lines. Indentation should be
used to indicate a related block of code.
In addition to good code commenting and writing readable code, the
following is a list of best practices in other areas, such as use of directives
and use of variables and constants:
Option Explicit Alway use the Option Explicit in every module. This
statement forces you to explicitly declare all the variables you use.
Option Base 1 The Option Base 1 statement causes the lower bound of
all array variables whose lower bound has not been specified to have a
lower bound of 1. This statement should not be used.
Option Compare Text The Option Compare Text statements causes all
string comparisons within the module to be text-based rather than
binary. This statement should be avoided.
Reuse variables Avoid reusing variables. Each variable should serve one
purpose only.
Variant data type Avoid using the Variant data type for the following rea-
sons: first, variants are not efficient; second, data stored in a variant can
behave unexpectedly.
Type conversion VBA automatically converts one data type to another data
type. Do not mix variables of different data types in your VBA expres-
sions without using the explicit casting functions (e.g., CStr and CDbl).
Array bounds Never hand-code the array bounds in a loop. Always use the
LBound function and the UBound function to get the bounds of an array.
ByRef and ByVal There are two conventions to declare arguments of a pro-
cedure: ByRef and ByVal. If an argument is declared with the ByRef
convention, then the memory address of the variable rather than the
value of the variable is passed. In such cases, modification of the vari-
able will be visible in the calling procedure. If an argument is declared
Summary 125
with the ByVal convention, the value of the variable is passed. Always
explicitly declare procedure arguments as ByRef or ByVal.
Validate the data types of selection Always check the object type of the
selection using the TypeName function or the TypeOf-Is structure.
Saving versions Use a version control system that allows you to recover an
earlier version of your project if you have encountered problems with
your current version.
For detailed discussion of the above best practices, readers are referred to
(Bovey et al., 2009, Chapter 3).
6.4 Summary
In this chapter, we introduced how to handle runtime errors and how to
use VBA’s debugging tools to identify potential logic errors. We also intro-
duced some best practices of VBA coding. For more information about best
practices, readers are referred to (Bovey et al., 2009).
Part II
Applications
7
Generating Payment Schedules
From 7.1, we see that the interface contains six input cells highlighted
in yellow and one button. The output is shown in the first two columns. The
first column shows the payment dates, and the second column shows the
year fractions between these dates. The goal of this chapter is to implement
the payment generator.
129
130 Generating Payment Schedules
7.1 Introduction
A derivative (Garrett, 2013) is a concept in finance and refers to a contract
that derives its value from the performance of an underlying entity, such
as an asset, index, or interest rate. Derivatives have been used for risk man-
agement purposes. For example, insurance companies have used interest
rate swaps to hedge the interest rate risks associated with variable annuity
contracts.
An interest rate swap (IRS) is a financial derivative in which two parties
agree to exchange interest rate cash flows, based on a specified notional
amount. Under an IRS, each counterpart agrees to pay either a fixed or
floating rate denominated in a particular currency to the other counterpart.
When pricing an IRS, one of the first steps is to generate a schedule of
dates, based on the holiday calendar and the date rolling rules specified
in the contract. The schedule of dates is used to project cash flows of the
IRS. Projecting cash flows is also dependent on the day count convention
specified in the swap contract.
In this chapter, we introduce how to generate a schedule of dates based
on the US holiday calendar. We will also introduce some day count conven-
tions and date rolling rules.
Name Date
New Year’s Day January 1
(January 2 if January 1 is a Sunday,
December 31 if January 1 is a Saturday)
Martin Luther King The third Monday of January
Jr. Day
Washington’s Birthday The third Monday of February
Memorial Day The last Monday of May
Independence Day July 4
(July 5 if July 4 is a Sunday,
July 3 if July 4 is a Saturday )
Labor Day The first Monday of September
Columbus Day The second Monday of October
Veterans Day November 11
(November 12 if November 11 is a Sunday,
November 10 if November 11 is a Saturday)
Thanksgiving Day The fourth Thursday of November
Christmas Day December 25
(December 26 if December 25 is a Sunday,
December 24 if December 25 is a Saturday)
1752 and is used throughout the world for secular purpose. The average
length of the year in the Gregorian calendar is 365.2425 days.
A date consists of three components: a year, a month, and a day of the
month. The three components correspond to the three principle astronomi-
cal cycles: the revolution of the Earth around the sun (a year), the revolution
of the moon around the Earth (a month), and the rotation of the Earth on its
axis (a day). The complexity of calendars comes from the fact that a month
and a year do not comprise an integral number of days, and they are neither
constant nor perfectly commensurable with each other.
(a) h = M − m
(b) g = Y + y − (n − h)\n
(c) f = mod (h − 1 + n, n)
(d) e = (pg + q)\r + D − 1 − j
(e) J = e + (s f + t )\u − (3 ∗ ((g + A)\100))\4 −C ,
The parameters used in the above two algorithms are given in Table 7.2.
where the days in the numerator are calculated on a Julian day difference
basis. Similarly, the ACT/360 day count convention is defined as follows:
360(Y2 − Y1 ) + 30(M 2 − M 1 ) + (D 2 − D 1 )
∆= , (7.4)
360
where
• Y1 is the year in which the first day of the calculation period falls;
• Y2 is the year in which the day immediately following the last day in-
cluded in the calculation period falls;
• M 1 is the month in which the first day of the calculation period falls;
• M 2 is the month in which the day immediately following the last day
included in the calculation period falls;
• D 1 is the first day of the calculation period. If the first day is 31, then
D 1 = 30;
• D 2 is the day immediately following the last day included in the calcula-
tion period. If D 1 = 30 and the day immediately following the last day is
31, then D 2 = 30.
to the next business day unless next business day is in the next calendar
month, in which case the payment date is adjusted to the previous business
day. If “preceding” is specified, the payment date is adjusted to the previous
business day.
Exercise 7.2. The 2016 holidays observed by the NYSE markets are: January
1, January 18, February 15, March 25, May 30, July 4, September 5, November
24, and December 26. Adjust the following payment dates according to
various business day conventions:
7.6 Implementation
In this section, we implement the payment schedule generator with the
interface shown in Figure 7.1. We would like to put the VBA code into three
modules: MInterface, MDate, and MHoliday. The module MInterface
contains VBA code for the button. The module MDate contains VBA code
for manipulating dates. The module MHoliday contains code for handling
business calendars and generating payment date schedules.
In the above code, we named the constants with the prefix “con” plus the
parameter names given in Table 7.2 so that we can easily connect the VBA
constants to the parameters. These constants are used to interconvert Gre-
gorian dates and Julian day numbers.
Once we have defined the constants in the module, we can implement
the function for converting a Gregorian date to a Julian day number. Fol-
lowing the algorithm given in Section 7.3, we implement the function as
follows:
1 Function ToJulian ( ByVal datIn As Date ) As Long
2 Dim lD As Long
3 Dim lM As Long
4 Dim lY As Long
5
6 Dim lh As Long
7 Dim lg As Long
8 Dim lf As Long
9 Dim le As Long
10
11 lD = Day ( datIn )
12 lM = Month ( datIn )
13 lY = Year ( datIn )
14
15 lh = lM - conm
16 lg = lY + cony - ( conn - lh ) \ conn
17 lf = ( lh - 1 + conn ) Mod conn
18 le = ( conp * lg + conq ) \ conr + lD - 1 - conj
19 ToJulian = le + ( cons * lf + cont ) \ conu - (3 *
(( lg + conA ) \ 100) ) \ 4 - conC
20 End Function
Implementation 137
In the above sub procedure, we first converted a date to a Julian day num-
ber and then converted the Julian day number back to a date. Executing
the above sub procedure produces the following output in the Immediate
window:
1 2450815
2 1/1/1998
3 2457510
4 5/1/2016
From the above output, we see that we can recover the dates exactly from
their Julian day numbers.
Now, let us implement functions for adding intervals (e.g., days, months,
years) to a date. Let us first implement the function AddDays, which returns
a date by adding a given number of days to an input date. We can implement
the function as follows:
1 Function AddDays ( ByVal datIn As Date , ByVal lCount As
Long ) As Date
2 Dim lJ As Long
3
4 lJ = ToJulian ( datIn ) + lCount
5 AddDays = FromJulian ( lJ )
6 End Function
To add days to a date, we first convert the date to a Julian day number and
add the given number of days to the Julian day number. Then we convert
the resulting Julian day number to a Gregorian date.
To test the function AddDays, we use the following sub procedure:
1 Sub TestAddDays ()
2 Debug . Print AddDays (#2/1/2016# , 28)
3 Debug . Print AddDays (#2/1/2016# , 365)
4 Debug . Print AddDays (#2/1/2016# , -365)
5 End Sub
The above output shows that the function AddDays works as expected.
Implementation 139
In the above test function, we used the Fot-Next loop to display the number
of days in each month of years 2016 and 2017. Executing the above code
gives:
1 31 29 31 30 31 30 31 31 30 31 30 31
2 31 28 31 30 31 30 31 31 30 31 30 31
From the output, we see that the function calculates the number of days
correctly.
Then we can implement the function for adding months as follows:
140 Generating Payment Schedules
The above output shows that the function AddMonths works correctly.
Similarly, we can implement the function for adding years as follows:
1 Function AddYears ( ByVal datIn As Date , ByVal lCount
As Long ) As Date
2 Dim lD As Long
3 Dim lM As Long
4 Dim lY As Long
5 Dim lDMax As Integer
6 Dim datTmp As Date
7
8 lD = Day ( datIn )
9 lM = Month ( datIn )
10 lY = Year ( datIn ) + lCount
11
12 ' handle Feb 29
13 datTmp = DateSerial ( lY , lM , 1)
14 lDMax = DaysInMonth ( datTmp )
15 If lD > lDMax Then
16 lD = lDMax
17 End If
18 AddYears = DateSerial ( lY , lM , lD )
19 End Function
From the output, we see that the function AddYears works well.
We can combine the functions for adding intervals into a single function
as follows:
1 Function OffsetDate ( ByVal datIn As Date , ByVal lCount
As Long , ByVal sPeriod As String ) As Date
2 Select Case sPeriod
3 Case " M "
4 OffsetDate = AddMonths ( datIn , lCount )
5 Case " Y "
6 OffsetDate = AddYears ( datIn , lCount )
7 Case Else
8 Err . Raise Number :=1003 , Source := " MHoliday
. GenerateDate " , Description := " Invalid
period string "
9 End Select
10 End Function
Exercise 7.3. The function OffsetDate can handle only two frequencies:
"M" and "Y." Modify the function OffsetDate so that it can handle the
following additional frequencies: "D," "W," and "Q," where D, W, and Q refer
to day, week, and quarter.
Now, let us implement functions for calculating the year fractions based
on the four day count conventions. We first implement a function that tells
whether a year is a leap year as follows:
1 Function IsLeapYear ( ByVal lY As Long ) As Boolean
2 Dim bRes As Boolean
3
4 bRes = False
5 If lY Mod 4 = 0 And lY Mod 100 <> 0 Then
6 bRes = True
Implementation 143
7 End If
8 If lY Mod 400 = 0 Then
9 bRes = True
10 End If
11
12 IsLeapYear = bRes
13 End Function
The idea behind the above implementation is that we add all the days in
leap years between the years of the two given dates. Then, we subtract the
days outside the two given dates. To this this function, we use the following
sub procedure:
1 Sub Te s t Da y sI n L ea p Ye a rs ()
2 Debug . Print DaysInLeapYears (#1/1/2016# ,
#1/31/2016#)
3 Debug . Print DaysInLeapYears (#1/1/2016# ,
#1/31/2018#)
4 Debug . Print DaysInLeapYears (#1/1/2015# ,
#1/31/2018#)
5 Debug . Print DaysInLeapYears (#1/1/2015# ,
#12/31/2015#)
6 End Sub
The output shows that the function calculates the number of days in leap
years correctly.
Using the function DaysInLeapYears, we can calculate the year frac-
tion based on the ACT/ACT convention as follows:
Implementation 145
24 lD2 = 30
25 End If
26 Thirty360 = lY2 - lY1 + ( lM2 - lM1 ) / 12# + ( lD2
- lD1 ) / 360#
27 End Function
The sign # after an integer tells VBA to treat the integer as a double.
We can combine the above two functions in the following function:
1 Function TFrac ( ByVal datA As Date , ByVal datB As Date
, ByVal sDCC As String ) As Double
2 Dim lJA As Long
3 Dim lJB As Long
4 lJA = ToJulian ( datA )
5 lJB = ToJulian ( datB )
6
7 If lJA > lJB Then
8 TFrac = TFrac ( datB , datA , sDCC )
9 Exit Function
10 End If
11
12 Select Case sDCC
13 Case " ACTACT "
14 TFrac = ActAct ( datA , datB )
15 Case " ACT365 "
16 TFrac = ( lJB - lJA ) / 365#
17 Case " ACT360 "
18 TFrac = ( lJB - lJA ) / 360#
19 Case " Thirty360 "
20 TFrac = Thirty360 ( datA , datB )
21 Case Else
22 Err . Raise Number :=1007 , Source := " MDate .
TFrac " , Description := " Unknown day
count convention . "
23 End Select
24 End Function
The function TFrac calculates the year fraction between two dates accord-
ing to a specified day count convention. The function will raise an error if an
unknown day count convention is encountered. We can test this function
as follows:
1 Sub TestTFrac ()
2 Dim datA As Date
3 Dim datB As Date
Implementation 147
4
5 datA = #12/31/2015#
6 datB = #12/31/2016#
7
8 Debug . Print TFrac ( datA , datB , " ACTACT " )
9 Debug . Print TFrac ( datA , datB , " ACT365 " )
10 Debug . Print TFrac ( datA , datB , " ACT360 " )
11 Debug . Print TFrac ( datA , datB , " Thirty360 " )
12 End Sub
The function IsWeekend returns true if the input date is a weekend. We can
test this function as follows:
1 Sub TestIsWeekend ()
2 Dim i As Integer
3 For i = 1 To 7
4 Debug . Print IsWeekend ( DateSerial (2016 , 5 , i ) )
& " ";
148 Generating Payment Schedules
5 Next i
6 End Sub
The function IsHolidayNY returns true if the input date is a public holiday
in the United States. Since a weekend cannot be a holiday, the function
returns false if the input date falls on a weekend. This function is imple-
mented according to the public holidays given in Table 7.1. We can test this
function as follows:
1 Sub TestIsHolidayNY ()
2 Dim datA As Date
3 Dim i As Integer
4
5 For i = 0 To 365
6 datA = AddDays ( DateSerial (2016 , 1 , 1) , i )
7 If IsHolidayNY ( datA ) Then
8 Debug . Print datA
9 End If
10 Next i
11
12 End Sub
The above sub procedure displays all the holidays in 2016. Executing the
sub procedure gives the following output:
1 1/1/2016
2 1/18/2016
3 2/15/2016
4 5/30/2016
5 7/4/2016
6 9/5/2016
7 10/10/2016
8 11/11/2016
9 11/24/2016
10 12/26/2016
If we check the US holidays in the Internet, we see that the above list of
holidays is correct.
The third function in this module checks whether a date is a business
day based on a calendar. We implement this function as follows:
1 Function IsBusinessDay ( ByVal datIn As Date , ByVal
sCalendar As String ) As Boolean
2 Dim bRet As Boolean
3 bRet = False
4
5 Select Case sCalendar
6 Case " NY "
Implementation 151
4 datTmp = datIn
5 Select Case sBDC
6 Case " Follow "
7 Do While Not IsBusinessDay ( datTmp ,
sCalendar )
8 datTmp = AddDays ( datTmp , 1)
9 Loop
10 Case " Modified "
11 Do While Not IsBusinessDay ( datTmp ,
sCalendar )
12 datTmp = AddDays ( datTmp , 1)
13 Loop
14 If Month ( datTmp ) <> Month ( datIn ) Then
15 datTmp = datIn
16 Do While Not IsBusinessDay ( datTmp ,
sCalendar )
17 datTmp = AddDays ( datTmp , -1)
18 Loop
19 End If
20 Case " Preceding "
21 Do While Not IsBusinessDay ( datTmp ,
sCalendar )
22 datTmp = AddDays ( datTmp , -1)
23 Loop
24 Case Else
25 Err . Raise Number :=1006 , Source := " MHoliday
. RollDate " , Description := " Unknown
business day convention . "
26 End Select
27
28 RollDate = datTmp
29 End Function
From the above output, we see that the function produces correct results.
In our program, payment frequencies and horizons are represented by
strings that contain an integer representing a number of periods and a letter
representing a period. We need a function to extract the number of periods
and the period symbol from a single string, such as “1M” and “30Y.” We can
implement this function as follows:
1 Sub ExtractPeriod ( ByVal sIn As String , ByRef iNum As
Integer , ByRef sPeriod As String )
2 Dim iLen As Integer
3 Dim sNum As String
4 iLen = Len ( sIn )
5
6 If iLen < 2 Then
7 Err . Raise Number :=1003 , Source := " MHoliday .
ExtractPeriod " , Description := " Invalid
period string "
8 End If
9 sPeriod = Right ( sIn , 1)
10 sNum = Left ( sIn , iLen - 1)
11 If IsNumeric ( sNum ) Then
12 iNum = CInt ( sNum )
13 Else
14 Err . Raise Number :=1003 , Source := " MHoliday .
ExtractPeriod " , Description := " Invalid
period string "
15 End If
16 End Sub
dure can change their values. To test this function, we use the following sub
procedure:
1 Sub Tes tExtract Period ()
2 Dim iNum As Integer
3 Dim sPeriod As String
4
5 ExtractPeriod " 2 M " , iNum , sPeriod
6 Debug . Print iNum & " --- " & sPeriod
7
8 ExtractPeriod " 30 Y " , iNum , sPeriod
9 Debug . Print iNum & " --- " & sPeriod
10 End Sub
Name Refers To
BusinessDayConvention =Schedule!$E$6
Calendar =Schedule!$E$5
DayCountConvention =Schedule!$E$7
Frequency =Schedule!$E$3
Horizon =Schedule!$E$4
SetDate =Schedule!$E$2
When the button is clicked, the sub procedure Button1_Click will run.
This sub procedure is an entry point of our program. Let us look at this
sub procedure. In Lines 2–9, we declare some variables. In Lines 13–18, we
make sure the value in the cell named “SetDate” is a date. In Lines 20–24,
we extract the inputs from relevant named cells. In Line 26, we call the
GenerateSchedule function, which is defined in the module MSchedule.
In Lines 28 and 33, we clear the content in the output area. In Lines 29–
31, we write the schedule of dates to the output area. In Lines 35–39, we
calculate the year fractions and write them to the output area.
To test the program, we use the inputs as shown in Figure 7.1. If we click
the button, we see that outputs as shown in Figure 7.1.
Exercise 7.4. Write a VBA function with two arguments that returns the
158 Generating Payment Schedules
Value Holiday
"NewYear" New Year’s Day
"MLK" Martin Luther King Jr. Day
"President" Present’s Day
"Memorial" Memorial Day
"Independence" Independence Day
"Labor" Labor Day
"Columbus" Columbus Day
"Veterans" Veterans Day
"Thanksgiving" Thanksgiving Day
"Christmas" Christmas Day
Exercise 7.5. Create the interface shown in Figure 7.2 and write a sub proce-
dure for the button so that clicking the button will generate the US holiday
dates and the observed holiday dates for any given year. When the year is
2017, the output should be identical to what shown in Figure 7.2.
Exercise 7.6. The date of the Easter Sunday in a year Y is a day in March of
the year Y . Suppose that March S, Y is the date of Easter Sunday in year Y .
Then the day S is determined as follows:
Use your function to calculate Easter Sunday from 2010 to 2020. If your
function is implemented correctly, the dates should match the dates given
in Table 7.4.
h
7.7 Summary
Generating a schedule of payment dates is usually the first step of valuing an
interest rate derivative, such as swaps. In this chapter, we introduced how
160 Generating Payment Schedules
161
162 Bootstrapping Yield Curves
Name Refers To
Param =Curve!$E$3:$E$12
Rate =Curve!$B$3:$C$10
8.1 Introduction
The yield curve, also known as the term structure of interest rates, is the
curve that shows the relation between the interest rate and the time to
maturity. For example, Table 8.2 gives the interest rates on different times
to maturities (or tenors). If we plot the interest rates against the tenors, we
see the yield curve as shown in Figure 8.2.
Tenor Rate
1 Year 0.69%
2 Year 0.77%
3 Year 0.88%
4 Year 1.01%
5 Year 1.14%
7 Year 1.39%
10 Year 1.68%
30 Year 2.21%
The interest rates shown in Table 8.2 and Figure 8.2 are continuously
compounded spot rates or zero-coupon rates. The continuously com-
pounded spot rate with maturity T is defined as
ln P (T )
R(T ) = − , (8.1)
τ(0, T )
2.0
l
1.5
l
1.0
0 5 10 15 20 25 30
Year
Let 0 ≤ T < S. Then, the forward rate for the period [T, S] is defined as
ln P (T ) − ln P (S)
R(T, S) = , (8.2)
τ(T, S)
where τ(T, S) is the year fraction between time T and time S according to
some day count convention. The simply compounded forward rate for the
period [T, S] is defined as
P (T )
µ ¶
1
F (T, S) = −1 . (8.3)
τ(T, S) P (S)
The discount factor is the factor that is used to multiply a future cash
flow in order to obtain the present value. The discount factor used to ob-
tained the present value of a cash flow at time T is defined as
8.2 Interpolation
Interest rate products in the market have only a few different maturities. In
order to construct a yield curve, we need to know the interest rates at all
tenors within a time horizon (e.g., 30 years). Interpolation is used to estimate
the interest rates at other maturities. Interpolation is also connected to the
bootstrapping method described in Section 8.3.
There are several interpolation methods (Hagan and West, 2006, 2008):
linear on rates, linear on the log of rates, linear on discount factors, linear on
the log of discount factors, piecewise linear forward, and splines. The linear
on the log of discount factors method, also known as the raw interpolation
method, is stable and straightforward to implement. In this program, we
use the raw interpolation method.
Let Ti < Ti +1 be two maturities of which we know the discount factors.
Let T ∈ [Ti , Ti +1 ]. Then, in the raw interpolation method, the discount
factor DF (T ) at time T is interpolated from the discount factors DF (Ti )
and DF (Ti +1 ) as follows:
Table 8.3: US Swap Rates for Various Maturities on February 8, 2016. These
rates were obtained from http://www.federalreserve.gov
Tenor Rate
1Y 0.69%
2Y 0.77%
3Y 0.88%
4Y 1.01%
5Y 1.14%
7Y 1.38%
10Y 1.66%
30Y 2.15%
τ(Ti −1 , Ti )r,
166 Bootstrapping Yield Curves
where τ(Ti −1 , Ti ) is the year fraction between dates Ti −1 and Ti , and r is the
swap rate. The floating cash flow at the date Ti is calculated as
τ(Ti −1 , Ti )F (Ti −1 , Ti ),
where F (Ti −1 , Ti ) is the simply compounded forward rate for the period
[Ti −1 , Ti ] (see Equation (8.3)). The swap rate is determined to satisfy the
following equation:
n n
τ(Ti −1 , Ti )r DF (Ti ) = τ(Ti −1 , Ti )F (Ti −1 , Ti )DF (Ti ),
X X
(8.7)
i =1 i =1
where DF (Ti ) = P (Ti ) is the discount factor for the period [0, Ti ]. Since
n n µ P (T ¶
i −1 )
τ(Ti −1 , Ti )F (Ti −1 , Ti )DF (Ti ) =
X X
− 1 P (Ti )
i =1 i =1 P Ti
X n
= [P (Ti −1 ) − P (Ti )]
i =1
= P (T0 ) − P (Tn ),
According to Equation (8.8), we can solve the swap rate r given a yield
curve, because we can obtain the discount factors DF (T0 ), DF (T1 ), . . . , DF (Tn )
from the yield curve. The bootstrapping procedure reverses this process,
that is, we solve the discount factors DF (T0 ), DF (T1 ), . . . , DF (Tn ) given the
swap r observed in the market. However, there are many unknown discount
factors and only one equation (i.e., Equation (8.8)). How can we solve the
discount factors based on one equation? To solve this problem, we just treat
DF (Tn ) as an unknown in the equation and interpolate other unknown dis-
count factors from DF (Tn ) and another known discount factor, which can
be 1 (i.e., the discount factor at time 0) or a discount factor bootstrapped
from a swap rate for a shorter maturity.
Let T0 be the settlement date. Let Ti denote the payment date after 6i
months, for i = 1, 2, . . . , 60. In particular, the bootstrapping procedure works
as follows:
(a) Bootstrap the discount factor DF (T2 ) using the 1Y swap rate. In this step,
we need to interpolate the discount factors at T0 (settlement date) and
T1 (6 months) from 1 (the discount factor at time 0) and DF (T2 ). We use
a method described in Section 8.4 to solve DF (T2 ).
Finding Roots of an Equation 167
(b) Bootstrap the discount factor DF (T4 ) using the 2Y swap rate. In this
step, we already know the discount factor DF (T0 ), DF (T1 ), and DF (T2 )
from the previous step. We only need to interpolate the discount factor
DF (T3 ) from DF (T2 ) and DF (T4 ). We can use the same method to solve
DF (T4 ).
(c) Repeat the above process to bootstrap discount factors DF (T6 ), DF (T8 ),
DF (T10 ), DF (T14 ), DF (T20 ), and DF (T60 ) from the swap rates for matu-
rities 3Y, 4Y, 5Y, 7Y, 10Y, and 30Y.
where DF (T0 ) and DF (T1 ) are interpolated from 1 and DF (T2 ). If we treat
DF (T2 ) as an unknown x in the equation, then the above equation can be
written as
r τ(T0 , T1 )g (T1 , x) + r τ(T1 , T2 )x = g (T0 , x) − x,
or
r τ(T0 , T1 )g (T1 , x) + r τ(T1 , T2 )x − g (T0 , x) + x = 0,
where g (·, ·) denotes the interpolation function. Solving the above equation
for x gives the discount factor DF (T2 ).
In this section, we introduce Newton’s method, also known as the
Newton–Raphson method, to find a root of the following equation:
f (x) = 0.
Newton’s method is an iterative method (Press et al., 2002, Chapter 9). Let
x 0 and x 1 be two initial guesses of the root of the above equation. Then,
Newton’s method proceeds as follows:
f (x i )
x i +1 = x i − (x i − x i −1 ) , i = 1, 2, . . . . (8.9)
f (x i ) − f (x i −1 )
We stop the iterative process until some condition is met. For example, we
can stop the iterative process when |x i +1 − x i | < 1e − 10.
168 Bootstrapping Yield Curves
8.5 Implementation
As we can see from Figure 8.1, the interface of the bootstrapping tool con-
tains inputs, outputs, and one button. When we click the button, the yield
curve will be bootstrapped from the swap rates given in the input. The
Visual Basic for Applications (VBA) code behind the interface is organized
into five modules: MDate, MHoliday, MSchedule, MCurve, and MInterface.
The first three modules, MDate, MHoliday, and MSchedule, are imported
from the payment schedule generator implemented in Chapter 7. We will
implement the modules MCurve and MInterface in this section.
In the above code, we defined quite a few module-level variables that are
used to store the input information and the output yield curve. The swap
rates and their tenors are saved in the arrays aRate and aTenor, respectively.
The resulting yield curve is stored in the arrays aDate and aDF, respectively.
Implementation 169
The array aDate contains the maturity dates of the swap rates, and the array
aDF contains the discount factors at these maturity dates.
The first procedure of this module is the sub procedure Initialize,
which is used to initialize the module-level variables based on the input
information. It also calculates the settlement date based on the number of
settlement days and the business calendar. We implement this sub proce-
dure as follows:
1 Sub Initialize ( ByRef rngRate As Range , ByRef rngParam
As Range )
2 Dim iNumRates As Integer
3 Dim i As Integer
4 iNumRates = rngRate . Rows . Count
5
6 ReDim aTenor (1 To iNumRates )
7 ReDim aRate (1 To iNumRates )
8
9 For i = 1 To iNumRates
10 aTenor ( i ) = rngRate . Cells (i , 1) . Value
11 aRate ( i ) = rngRate . Cells (i , 2) . Value / 100
12 Next i
13
14 sFixFreq = rngParam . Cells (1 , 1) . Value
15 sFixDCC = rngParam . Cells (2 , 1) . Value
16 sFltFreq = rngParam . Cells (3 , 1) . Value
17 sFltDCC = rngParam . Cells (4 , 1) . Value
18 sCalendar = rngParam . Cells (5 , 1) . Value
19 sBDC = rngParam . Cells (6 , 1) . Value
20 datCurveDate = rngParam . Cells (7 , 1) . Value
21 iSetDays = rngParam . Cells (8 , 1) . Value
22 sYCDCC = rngParam . Cells (9 , 1) . Value
23 sOutputType = rngParam . Cells (10 , 1) . Value
24
25 datSet = datCurveDate
26 For i = 1 To iSetDays
27 datSet = RollDate ( AddDays ( datSet , 1) , " Follow
" , sCalendar )
28 Next i
29
30 ReDim aDate (0 To 0)
31 ReDim aDF (0 To 0)
32 aDate (0) = datCurveDate
33 aDF (0) = 1
34 End Sub
170 Bootstrapping Yield Curves
In this sub procedure, the settlement date is calculated, and the yield curve
is initialized. The output yield curve is stored in two array-type variables:
aDate and aDF. At the beginning, the variable aDate is initialized to contain
the curve date, and the variable aDF is initialized to contain 1, which is the
discount factor at the curve date.
For testing purposes, we also create the following sub procedure:
1 Sub PrintInfo ()
2 Dim i As Integer
3 For i = LBound ( aTenor ) To UBound ( aTenor )
4 Debug . Print " Swap Rate " & i & " : " & aTenor (
i ) & " -- " & aRate ( i )
5 Next i
6
7 Debug . Print " Fixed Leg : " & sFixFreq & " -- " &
sFixDCC
8 Debug . Print " Floating Leg : " & sFltFreq & " -- "
& sFltDCC
9 Debug . Print " Calendar : " & sCalendar
10 Debug . Print " Business Day Convention : " & sBDC
11 Debug . Print " Curve Date : " & datCurveDate
12 Debug . Print " Settlement Days : " & iSetDays
13 Debug . Print " Yield Curve Day Count Convention : "
& sYCDCC
14 Debug . Print " Settlement Date : " & datSet
15
16 For i = LBound ( aDate ) To UBound ( aDate )
17 Debug . Print " Discount Factor " & i & " : " &
aDate ( i ) & " -- " & aDF ( i )
18 Next i
19
20 End Sub
This sub procedure just displays the content of the module-level variables
in the Immediate Window.
To test the sub procedure Initialize, we use the following code:
1 Sub TestInitialize ()
2 Initialize rngRate := Range ( " Rate " ) , rngParam :=
Range ( " Param " )
3
4 PrintInfo
5 End Sub
Implementation 171
If we compare the above output to the inputs shown in Figure 8.1, we see
that the sub procedure Initialize works correctly.
We implement the raw interpolation method as follows:
1 Function LogLinear ( ByVal datD As Date ) As Double
2 If datD < datCurveDate Then
3 Err . Raise Number :=2001 , Source := " MCurve .
LogLinear " , Description := " Date is ealier
than the curve date . "
4 End If
5 Dim i As Integer
6 Dim iL As Integer
7 Dim iH As Integer
8 iL = LBound ( aDate )
9 iH = UBound ( aDate )
10 For i = iL To iH
11 If datD = aDate ( i ) Then
12 LogLinear = aDF ( i )
13 Exit Function
14 End If
15 Next i
16
17 Dim dat1 As Date
18 Dim dat2 As Date
19 Dim dDF1 As Double
172 Bootstrapping Yield Curves
The function procedure LogLinear has one argument, which is a date. The
function will raise an error if the input date is older than the curve date. If
the input date is equal to one of the dates stored in the variable aDate, the
function returns the discount factor corresponding to this date. If the date
is not equal to any dates stored in the variable aDate, the function returns
the discount factor interpolated by the raw interpolation method.
To test the function LogLinear, we use the following sub procedure:
1 Sub TestLogLinear ()
Implementation 173
2 ReDim aDate (0 To 2)
3 ReDim aDF (0 To 2)
4
5 aDate (0) = #4/5/2016#
6 aDate (1) = #4/5/2017#
7 aDate (2) = #4/5/2026#
8 aDF (0) = 1
9 aDF (1) = 0.95
10 aDF (2) = 0.7
11
12 sYCDCC = " Thirty360 "
13
14 Debug . Print LogLinear (#4/5/2016#)
15 Debug . Print LogLinear (#10/5/2016#)
16 Debug . Print LogLinear (#4/5/2041#)
17 End Sub
15 Next i
16
17 ' calculate floating leg cash flows and present
value
18 Dim vFltDates As Variant
19 Dim dFltPV As Double
20 Dim dFR As Double
21 Dim dT As Double
22 ' calculate fixed leg cash flows and present
value
23 vFltDates = GenerateSchedule ( datSet , sFltFreq ,
sTenor , sCalendar , sBDC )
24 For i = LBound ( vFltDates ) + 1 To UBound ( vFltDates
)
25 dDF = LogLinear ( vFltDates ( i ) )
26 dT = TFrac ( vFltDates ( i - 1) , vFltDates ( i ) ,
sFltDCC )
27 dFR = ( LogLinear ( vFltDates ( i - 1) ) /
LogLinear ( vFltDates ( i ) ) - 1) / dT
28 dFltPV = dFltPV + dT * dFR * dDF
29 Next i
30
31 PvSwap = dFixPV - dFltPV
32 End Function
The function procedure PvSwap has two arguments: the tenor and the rate
of the swap. This function uses some module-level variables, such as these
used to specify the payment frequency and the business calendar. It first
calculates the present value of the fixed leg, and then calculates the present
value of the floating leg.
To test the function PvSwap, we use the following sub procedure:
1 Sub TestPvSwap ()
2 Initialize rngRate := Range ( " Rate " ) , rngParam :=
Range ( " Param " )
3
4 ReDim Preserve aDate (0 To 1)
5 ReDim Preserve aDF (0 To 1)
6
7 aDate (1) = OffsetDate ( datSet , 1 , " Y " )
8 aDF (1) = 0.95
9
10 Debug . Print PvSwap ( aTenor (1) , aRate (1) )
11 End Sub
Implementation 175
If we use the inputs shown in Figure 8.1, then executing the above code
gives
1 -4.30769841682971 E -02
The output shows that the present value of the fixed leg is lower than that of
the floating leg.
We implement Newton’s method as follows:
1 Sub SolveRate ( ByVal iInd As Integer )
2 ReDim Preserve aDate (0 To iInd )
3 ReDim Preserve aDF (0 To iInd )
4
5 Dim iNum As Integer
6 Dim sPeriod As String
7
8 ExtractPeriod sIn := aTenor ( iInd ) , iNum := iNum ,
sPeriod := sPeriod
9 aDate ( iInd ) = OffsetDate ( datSet , iNum ,
sPeriod )
10
11 Dim dx As Double
12 Dim dx0 As Double
13 Dim dx1 As Double
14 Dim df As Double
15 Dim df0 As Double
16 Dim df1 As Double
17
18 dx0 = Exp ( - aRate ( iInd ) * TFrac ( datCurveDate ,
aDate ( iInd ) , sYCDCC ) )
19 aDF ( iInd ) = dx0
20 df0 = PvSwap ( aTenor ( iInd ) , aRate ( iInd ) )
21 dx1 = dx0 + 0.001
22 aDF ( iInd ) = dx1
23 df1 = PvSwap ( aTenor ( iInd ) , aRate ( iInd ) )
24 Do While Abs ( dx1 - dx0 ) > 0.0000000001
25 dx = dx0 - df0 * ( dx1 - dx0 ) / ( df1 - df0
)
26 aDF ( iInd ) = dx
27 df = PvSwap ( aTenor ( iInd ) , aRate ( iInd ) )
28
29 dx0 = dx1
30 df0 = df1
31 dx1 = dx
32 df1 = df
176 Bootstrapping Yield Curves
33 Loop
34
35 aDF ( iInd ) = dx
36 End Sub
The sub procedure SolveRate has only one argument, which is an integer
that represents the index of the input swap rates. This sub procedure also
modifies the variables used to store the yield curve. At the beginning of this
sub procedure, the variables aDate and aDF are redimensioned to contain
one more value. The existing values are preserved.
To test the sub procedure SolveRate, we use the following sub proce-
dure:
1 Sub TestSolveRate ()
2 Initialize rngRate := Range ( " Rate " ) , rngParam :=
Range ( " Param " )
3
4 SolveRate iInd :=1
5
6 Debug . Print aDate (1) & " -- " & aDF (1)
7 End Sub
If we use the inputs shown in Figure 8.1, then executing the above code
gives
1 2/10/2017 -- 0.993097540002 669
The output shows that the bootstrapped discount factor for 1 year is about
0.9931.
Now, we can implement a sub procedure to bootstrap all the discount
factors as follows:
1 Sub BuildCurve ()
2 Dim iL As Integer
3 Dim iH As Integer
4 Dim i As Integer
5 iL = LBound ( aTenor )
6 iH = UBound ( aTenor )
7
8 For i = iL To iH
9 SolveRate iInd := i
10 Next i
11 End Sub
Implementation 177
The sub procedure BuildCurve just loops through the input swap rates
and calls the sub procedure SolveRate to bootstrap the discount factors.
To test this sub procedure, we use the following sub procedure:
1 Sub TestBuildCurve ()
2 Dim i As Integer
3 Initialize rngRate := Range ( " Rate " ) , rngParam :=
Range ( " Param " )
4
5 BuildCurve
6 PrintInfo
7 End Sub
From the output, we see that the yield curve was constructed.
Finally, we implement a function to create output based on a specified
output type. We implement this function as follows:
178 Bootstrapping Yield Curves
38 CreateOutput = aOut
39 End Function
This sub procedure is responsible for preparing the input and displaying
the output. The sub procedure first calls other three functions: Initialize,
BuildCurve, and CreateOutput. The three functions are defined in the
module MCurve. Then, the sub procedure writes the output to columns 8
180 Bootstrapping Yield Curves
Exercise 8.2. The function procedure PvSwap in the module MCurve cal-
culates the present value of the floating leg by projecting and discounting
the individual cash flows. In fact, we do not have to do this. From Equation
(8.8), the net present value of a swap is given by
n
τ(Ti −1 , Ti )DF (Ti ) − DF (T0 ) + DF (Tn ).
X
r
i =1
Rewrite the function procedure PvSwap by using the above formula to cal-
culate the net present value.
Exercise 8.3. Newton’s method may not converge if the initial guesses are
not good. Another method for finding a root of an equation is the bisection
method. Suppose that we want to find a root of the equation f (x) = 0, where
f (x) is a continuous function of x. We first find two values x L < xU , such that
f (x L ) f (xU ) < 0. Then we know that there is a root in the interval (x L , xU ).
We can use the bisection method to find the root. To do that, we let
xL + x H
xM = .
2
If f (x L ) f (x M ) < 0, then we update x H = x M . If f (x M ) f (x H ) < 0, then we
update x L = x M . We continues updating x L and x H in the above way until
|x L − x H | < 10−10 .
Summary 181
8.6 Summary
Bootstrapping a yield curve allows us to produce swap prices that are con-
sistent with market prices. In this chapter, we introduced how to construct a
yield curve by bootstrapping discount factors from swap rates, which cover
a wide range of maturities. We also introduced an interpolation method
and Newton’s method for solving a nonlinear equation that is required in
the bootstrapping process. For more information about interpolation meth-
ods for constructing yield curves, readers are referred to (Hagan and West,
2006, 2008). Other methods for solving nonlinear equations can be found
in (Press et al., 2002; Garrett, 2015).
9
Generating Risk-Neutral Scenarios
The inputs of the scenario generator include a list of forward rates and
six parameters: the volatility, the horizon of the scenarios, the time step, the
number of paths, a seed, and the precision of the scenarios. The seed allows
us to repeat the scenarios. In other words, if we use the same seed and the
same other parameters, then we get the same set of scenarios.
183
184 Generating Risk-Neutral Scenarios
9.1 Introduction
Economic scenario generators have been used by insurance companies
to price insurance products (e.g., variable annuities) embedding guaran-
tees and calculate capital adequacy. An economic scenario generator is
simply a computer program that produces random sequences of market
variables (e.g., equity returns) by assuming that those market variables
follow some stochastic process. For a particular market variable, we call a
random sequence of the market variable a path or a scenario.
There are two types of economic scenarios that are commonly used by
insurance companies: risk-neutral scenarios and real-world scenarios. Risk-
neutral scenarios, also referred to as arbitrage-free or market-consistent
scenarios, are economic scenarios that are consistent with market prices.
Risk-neutral scenarios are generally used for pricing purpose. Real-world
scenarios are created based on the historical experience of market variables
and are used for other purposes, such as calculating capital adequacy and
profitability of a product.
where r s is the short rate at time s, σ is the volatility of the equity security,
and {B t : t ≥ 0} is a standard Brownian motion (Gan et al., 2014).
The stochastic process given in Equation (9.1) is a continuous-time
stochastic process. Since a path is a discrete sequence, we need to discretize
the continuous-time stochastic process. Let ∆ be a time step in years. For
1
example, if we want to generate monthly scenarios, then ∆ = 12 . If we
want to generate scenarios at the annual time step, then ∆ = 1. Let m be
the number of time steps of a path. For example, if we want to generate
Black–Scholes Model 185
scenarios at the monthly time step for a horizon of 30 years, then m = 360.
For i = 0, 1, . . . , m, let t i = ∆i . Then, by Equation (9.1), we have
∆
·µZ t
S ti
¶ ¸
i
= exp r s ds − σ2 + σ(B ti − B ti −1 ) (9.2)
S ti −1 t i −1 2
for i = 1, 2, . . . , m.
Let
S ti
Ai = , i = 1, 2, . . . , m. (9.3)
S ti −1
We call A i the accumulation factor for the i th period. In other words, A i is
the growth of the equity security from time t i −1 = (i − 1)∆ to time t i = i ∆.
Then, by Equation (9.2) and Equation (9.3), we have
∆
µZ t ¶
i
ln A i = r s ds − σ2 + σ(B ti − B ti −1 ), (9.4)
t i −1 2
1 P (t i −1 )
µ ¶
fi = −1 , (9.6)
∆ P (t i )
where P (t i ) is the price at time zero of the zero-coupon bond maturing at
time t i . Since the interest rate is deterministic rather than stochastic, we
have (Gan et al., 2014, Chapter 45)
µ Z t ¶
i
P (t i ) = exp − r s ds , i = 0, 1, . . . , m. (9.7)
0
which gives Z ti
r s ds = ln(1 + ∆ f i ). (9.9)
t i −1
Now, plugging Equation (9.5) and Equation (9.9) into Equation (9.4), we
get
∆ 2 p
ln A i = ln(1 + ∆ f i ) − σ + σ ∆Zi (9.10)
2
for i = 1, 2, . . . , m. We use Equation (9.10) to generate risk-neutral scenarios.
Let n be the number of paths or scenarios we want to generate. Then,
the scenarios can be represented by an n × m matrix defined as
A 11 A 12 · · · A 1m
A 21 A 22 · · · A 2m
, (9.11)
. .. ..
. ..
. . . .
A n1 A n2 · · · A nm
(x − µ)2
µ ¶
1
f (x, µ, σ) = p exp − , x ∈ (−∞, ∞), (9.13)
2πσ 2σ2
where µ is the mean, σ is the standard deviation, and exp(·) is the exponen-
tial function. The cumulative density function of the normal distribution is
defined as
Z y
F (y, µ, σ) = f (x, µ, σ)dx
−∞
(x − µ)2
Z y µ ¶
1
= p exp − dx, y ∈ (−∞, ∞). (9.14)
−∞ 2πσ 2σ2
Implementation 187
9.4 Implementation
From Figure 9.1, we see that the input and the output are organized into the
“Main” sheet and the “Scenarios” sheet, respectively. When the button is
clicked, the scenarios are written to the “Scenarios” sheet. We organize the
Visual Basic for Applications (VBA) code into two modules: the MGenerator
module and the MInterface module.
In the above code, we defined variables to store the parameters and the
forward rates. These variables are private variables that can only be used by
procedures in this module.
The following sub procedure, named Initialize, is used to initialize
the variables declared above:
188 Generating Risk-Neutral Scenarios
1 Sub Initialize ( _
2 ByVal Sigma As Double , _
3 ByVal Horizon As String , _
4 ByVal TimeStep As String , _
5 ByVal NumPath As Long , _
6 ByVal Seed As Long , _
7 ByVal Precision As Integer , _
8 ByRef FR () As Double )
9 dSigma = Sigma
10 sHorizon = Horizon
11 sTimeStep = TimeStep
12 lNumPath = NumPath
13 lSeed = Seed
14 iPrecision = Precision
15
16 Dim lL As Long
17 Dim lU As Long
18 lL = LBound ( FR )
19 lU = UBound ( FR )
20
21 ReDim aFR (1 To lU - lL + 1)
22 Dim i As Long
23 For i = lL To lU
24 aFR ( i - lL + 1) = FR ( i )
25 Next i
26
27 End Sub
In the above sub procedure, the forward rates are passed by reference.
Other parameters are passed to the procedure by value. In Line 21, we re-
dimension the module-level array aFR to hold the forward rates. In Lines
23–25, we copy the forward rates to the array.
To make sure the sub procedure Initialize works as expected, we
need to test it. Before testing it, we need a sub procedure to display the
contents of the module-level variables. We can write such a sub procedure
as follows:
1 Sub PrintInfo ()
2 Debug . Print " Volatility : " & CStr ( dSigma )
3 Debug . Print " Horizong : " & sHorizon
4 Debug . Print " TimeStep : " & sTimeStep
5 Debug . Print " NumPath : " & CStr ( lNumPath )
6 Debug . Print " Seed : " & CStr ( lSeed )
Implementation 189
The sub procedure PrintInfo displays the values of the module-level vari-
ables in the Immediate window. Instead of displaying all the forward rates
in the array, we only show the number of forward rates and the first six
forward rates.
Now, we can use the following sub procedure to test the sub procedure
Initialize:
1 Sub TestInitialize ()
2 Dim rngParam As Range
3 Dim rngFirstFR As Range
4
5 Set rngParam = Sheet1 . Range ( " Param " )
6 Set rngFirstFR = Sheet1 . Range ( " FirstFR " )
7
8 Dim aFR () As Double
9 Dim lFirstRow As Long
10 Dim lLastRow As Long
11
12 lFirstRow = rngFirstFR . Cells (1 , 1) . Row
13 lLastRow = rngFirstFR . Cells (1 , 1) . End ( xlDown ) . Row
14
15 ReDim aFR (1 To lLastRow - lFirstRow + 1)
16 Dim i As Long
17 For i = 1 To lLastRow - lFirstRow + 1
18 aFR ( i ) = rngFirstFR . Cells (1 , 1) . Offset ( i - 1 ,
0) . Value
19 Next i
20
21 Initialize Sigma := rngParam . Cells (1 , 1) . Value , _
190 Generating Risk-Neutral Scenarios
The test sub procedure requires two named ranges, which are shown in
Table 9.1. Since the number of forward rates varies, we only refer to the first
forward rate. We assume that the forward rates are organized continuously
in a column. In Lines 12–13, we calculate the first row and the last row
of the forward rates. In Lines 17–19, we copy the forward rates from the
worksheet to a procedure-level array named aFR. In Lines 21–27, we call
the sub procedure Initialize with appropriate arguments. Line 29 calls
the sub procedure PrintInfo to display the contents of the module-level
variables. Executing the above code, we see the following output in the
Immediate window:
1 Horizong : 30 Y
2 TimeStep : 1 M
3 NumPath : 100
4 Seed : 2016
5 Precision : 6
6 Number of Forward Rates : 360
7 0.00689 0.00689 0.00689 0.00689 0.00689 0.00689
Comparing the above output and Figure 9.1, we see that the sub procedure
Initialize works well.
Table 9.1: Range Names Defined in the Interface of the Risk-Neutral Scenario
Generator
Name Refers To
FirstFR =Main!$A$2
Param =Main!$D$2:$D$7
Note that the forward rates depend on the time step. In the above test,
we used a monthly time step. The forward rates are monthly forward rates
obtained from the yield curve bootstrapper (see Chapter 8). If the time step
Implementation 191
is “1Y,” we need to input annual forward rates, which can also be obtained
from the yield curve bootstrapper by specifying the “Output Type” to “1Y.”
To generate scenarios, we need first to figure out the number of time
steps and the ∆ required by Equation (9.10). To do that, we need a procedure
to extract the number of periods and the period string from input strings,
such as “30Y” and “1M.” For this purpose, we can use the following sub
procedure:
1 Sub ExtractPeriod ( ByVal sIn As String , ByRef iNum As
Integer , ByRef sPeriod As String )
2 Dim iLen As Integer
3 Dim sNum As String
4 iLen = Len ( sIn )
5
6 If iLen < 2 Then
7 Err . Raise Number :=1003 , Source := " MHoliday .
ExtractPeriod " , Description := " Invalid
period string "
8 End If
9 sPeriod = Right ( sIn , 1)
10 sNum = Left ( sIn , iLen - 1)
11 If IsNumeric ( sNum ) Then
12 iNum = CInt ( sNum )
13 Else
14 Err . Raise Number :=1003 , Source := " MHoliday .
ExtractPeriod " , Description := " Invalid
period string "
15 End If
16 End Sub
The above sub procedure was first implemented in the yield curve boot-
strapper. For an explanation of this sub procedure, readers are referred to
Chapter 8.
Now, we can calculate the number of time steps using the following
function:
1 Private Function CalNumTimeStep ( ByVal Horizon As
String , ByVal TimeStep As String ) As Long
2 Dim lRet As Long
3
4 Dim iHorizonNum As Integer
5 Dim sHorizonPeriod As String
6 Dim iStepNum As Integer
7 Dim sStepPeriod As String
192 Generating Risk-Neutral Scenarios
8
9 ExtractPeriod sIn := Horizon , iNum := iHorizonNum ,
sPeriod := sHorizonPeriod
10 ExtractPeriod sIn := TimeStep , iNum := iStepNum ,
sPeriod := sStepPeriod
11
12 If Not StrComp ( sHorizonPeriod , " Y " , vbTextCompare
) = 0 Then
13 Err . Raise Number :=1001 , Source := " MGenerator .
CalNumTimeStep " , _
14 Description := " Horizon should be specified
in years . "
15 End If
16
17 If StrComp ( sStepPeriod , " Y " , vbTextCompare ) = 0
Then
18 lRet = iHorizonNum / iStepNum
19 ElseIf StrComp ( sStepPeriod , " M " , vbTextCompare ) =
0 Then
20 lRet = 12 * iHorizonNum / iStepNum
21 Else
22 Err . Raise Number :=1002 , Source := " MGenerator .
CalNumTimeStep " , _
23 Description := " Unknown combination of
period strings . "
24 End If
25
26 CalNumTimeStep = lRet
27 End Function
1 30
2 360
3 40
4 51
From the output, we see that the function calculates the number of time
steps correctly.
To calculate the time step ∆, we use the following function:
1 Private Function CalDelta ( ByVal TimeStep As String )
As Double
2 Dim dRet As Double
3
4 Dim iStepNum As Integer
5 Dim sStepPeriod As String
6
7 ExtractPeriod sIn := TimeStep , iNum := iStepNum ,
sPeriod := sStepPeriod
8
9 If StrComp ( sStepPeriod , " Y " , vbTextCompare ) = 0
Then
10 dRet = iStepNum
11 ElseIf StrComp ( sStepPeriod , " M " , vbTextCompare ) =
0 Then
12 dRet = iStepNum / 12#
13 Else
14 Err . Raise Number :=1004 , Source := " MGenerator .
CalDelta " , _
15 Description := " Unknown time step string . "
16 End If
17
18 CalDelta = dRet
19 End Function
The function CalDelta is also a private function that can only be used
within this module. This version can only handle time steps specified in
months or years. In Line 12, the sign # after 12 indicates that 12 is treated as
a double-type number rather than an integer.
To test the function CalDelta, we use the following sub procedure:
1 Sub TestCalDelta ()
2 Debug . Print CalDelta ( " 1 Y " )
3 Debug . Print CalDelta ( " 1 M " )
4 Debug . Print CalDelta ( " 3 M " )
5 End Sub
194 Generating Risk-Neutral Scenarios
Executing the above test sub procedure gives the following output:
1 1
2 8.33333333333333 E -02
3 0.25
The code in the first part of this sub procedure is similar to that of the sub
procedure TestInitialize implemented in the module MGenerator. In
Lines 21–30, the sub procedure calls Initialize and Generate to generate
scenarios. In Lines 35–39, it writes the scenarios to Sheet2.
We assign the sub procedure Button1_Click to the button in the inter-
face. If we use the parameters and forward rates shown in Figure 9.1, we
will get the scenarios shown in Figure 9.2.
g
Exercise 9.1. The volatility is a positive real number. However, we did not
check the positivity of the volatility parameter in our implementation. Add
code to the sub procedure Initialize to check the positivity of the volatil-
ity.
Exercise 9.2. The scenarios are saved to the worksheet as accumulation
factors A i j for i = 1, 2, . . . , n and j = 1, 2, . . . , m, where n is the number of
paths, and m is the number of time steps. Add code to the sub procedure
Button1_Click to do the following:
• For each j = 1, 2, . . . , m, calculate the mean µ j and the standard devia-
tion σ j of ln A 1 j , ln A 2 j , . . ., ln A n j , that is, calculate the mean and the
Summary 197
Figure 9.2: First few rows and columns of the risk-neutral scenarios
9.5 Summary
In this chapter, we implemented a simple risk-neutral scenario generator
based on the Black–Scholes model. The scenario generator generates risk-
198 Generating Risk-Neutral Scenarios
199
200 Valuing a GMDB
shifted 10bps down, respectively. The three sets of scenarios are saved in
the worksheets Base, AllUp, and AllDn, respectively.
10.1 Introduction
A variable annuity is a long-term life insurance contract between a policy-
holder and an insurance company under which the policyholder agrees to
make a lump-sum purchase payment or a series of purchase payments to
the insurance company, and, in return, the insurance company agrees to
make benefit payments to the policyholder, beginning immediately or at a
future date (The Geneva Association Report, 2013). Variable annuities are
known as unit-linked products in Europe and segregated funds in Canada.
Under a variable annuity contract, the policyholder can allocate the
account value to several subaccounts of the insurance company’s separate
account. The separate account of an insurance company is managed sepa-
rately from the insurance company’s general account and is protected from
claims against the insurance company in the event of insolvency. Subac-
counts are similar to mutual funds and allow the policyholder to participate
in the stock market.
One main feature of variable annuities is that they contain guarantees,
which include GMDB and guaranteed minimum living benefits (GMLB).
The death benefit is paid to the beneficiary of a policyholder upon the death
of the policyholder. There are several types of GMLB: guaranteed minimum
accumulation benefits (GMAB), guaranteed minimum maturity benefits
(GMMB), guaranteed minimum income benefits (GMIB), and guaranteed
minimum withdrawal benefits (GMWB). The GMAB rider provides a guaran-
teed minimum survival benefit during a specified window after a specified
waiting period, which is usually 10 years. The specified widows typically
begins on an anniversary date and remains open for 30 days (Brown et al.,
2002). The GMMB rider is similar to the GMAB rider, but the guaranteed
amount is only available at the maturity of the contract. The GMIB rider
gives a policyholder annuitized payments during a specified window after a
specified waiting period. The annuitized payments are equal to the greater
of payments determined by applying current fixed-annuitization rates to
the payments determined by applying guaranteed fixed-annuitization rates,
which are set at issue. The GMWB rider gives a policyholder the right to
Life Table Construction 201
withdrawal a specified amount during the life of the contract until the initial
investment is recovered.
Such guarantees are financial guarantees and cannot be addressed
adequately by traditional actuarial approaches (Boyle and Hardy, 1997).
Dynamic hedging is a popular approach to managing the financial risk as-
sociated with the guarantees and is adopted by some insurance companies.
However, dynamic hedging requires calculating the Greeks (i.e., sensitivities
of the value of guarantees on market factors) of the guarantees. Due to the
complexity of guarantees, the Monte Carlo simulation method is usually
used to value the guarantees. In the Monte Carlo simulation method, the
cash flows of the guarantees are projected along many risk-neutral paths
(see Chapter 9), and the value of the guarantees is calculated as the average
present value of the cash flows.
The cash flows of different types of guarantees are projected differently,
depending on the contract specifications. In this chapter, we introduce how
to value a GMDB using the Monte Carlo simulation method.
where [x] and {x} represent the integer part and the fractional part of x,
respectively.
Under the linear interpolation method, t p x , the probability that a per-
202 Valuing a GMDB
The probability that a person aged x will die before the age of x + t is calcu-
lated as
t qx = 1 − t p x . (10.4)
DB i j denotes the claim of death benefit when the policyholder dies within
the period (t j −1 , t j ].
φM E denotes the annualized mortality and expense (M&E) fee of the con-
tract. This fee is charged as a percentage of the fund value at the begin-
ning of each anniversary.
GMDB Valuation 203
φI E denotes the initial expense used to cover the sales commission. This
fee is charged once at the inception of the policy.
φOE denotes the ongoing expense that is used to cover the administrative
cost. This expense is a fixed dollar amount charged at the beginning of
each anniversary.
DBSU contains a step-up GMDB rider. The death benefit is reset to the
account value annually if the latter is higher.
Suppose that the fees are charged from the account values at the end
of every period. For j = 0, 1, . . . , m − 1, the cash flows from t j to t j +1 are
projected as follows:
A i j F i , j +1 1 − ∆φF φM E , if t j +1 is an anniversary
½ ¡ ¢
RC i , j +1 =
0, if otherwise.
(10.6)
The risk charge at inception is calculated as
RC i ,0 = Premium × (1 − φI E )φM E .
204 Valuing a GMDB
GD i , j +1 = GD i , j , GD i ,0 = Premium. (10.7)
DB i , j +1 = max{0,GD i , j +1 − A i , j +1 }. (10.9)
• After the maturity of the contract, all the state variables are set to zero.
Once we have all the cash flows, we can calculate the fair market values
of the riders as follows:
1X n X m
V0 = ( j −1)∆ p x 0 · ∆ q x 0 +( j −1)∆ L j DB i , j d j , (10.10)
n i =1 j =1
Here, r L is the annual lapse rate, T is the maturity of the policy, and
f 1 , f 2 , . . . , f m are forward rates. The risk charge value is calculated as
1X n X m
RC 0 = j ∆ p x 0 RC i , j L j d j . (10.11)
n i =1 j =1
F MV0 = V0 − RC 0 . (10.12)
Greek Calculation 205
10.5 Implementation
We organize the Visual Basic for Applications (VBA) code of the valuation
program into three modules: the MLifeTable module, the MGMDB module,
and the MInterface module. A list of range names used by the program is
given in Table 10.1.
206 Valuing a GMDB
Name Refers To
FRAllDn =Main!$L$3:$L$362
FRAllUp =Main!$K$3:$K$362
FRBase =Main!$J$3:$J$362
MTFemale =Main!$G$3:$G$112
MTMale =Main!$F$3:$F$112
Param =Main!$B$3:$B$13
The sub procedure requires two arguments: a range containing male mor-
tality rates, and a range containing female mortality rates. In Lines 4–7, we
check whether the two input ranges have the same number of rows. If the
two ranges do not have the same number of rows, an error will be raised.
The maximum available age in the mortality rates is the number of rows
minus one, as the mortality rates start from age 0. In Line 10, we redimen-
sion the array aLT so that it can hold all the survival probabilities, which are
calculated in Lines 14–19.
g
Exercise 10.1. Write a sub procedure named PrintInfo to display the
values of the module-level variables in the Immediate window.
Exercise 10.2. Write a sub procedure named TestInitialize to test the
sub procedure Initialize. In particular, you first call the sub procedure
Initialize and then call PrintInfo (see Exercise 10.1) to show the values
of the module-level variables.
h
8 End If
9
10 iL = WorksheetFuncti on . Floor (x , 1)
11 iU = iL + 1
12
13 Interpolate = ( iU - x ) * aLT ( iL , g ) + ( x - iL ) *
aLT ( iU , g )
14 End Function
In Lines 6–8 of the above code, we first check that the input age is not
less than zero or larger than the maximum age. Since we use the linear
interpolation method, we cannot extrapolate the survival probabilities.
In Line 10, we get the integer part of the input age. The code in Line 13
implements the linear interpolation method based on Equation (10.2).
To test the function Interpolate, we use the following sub procedure:
1 Sub TestInterpolate ()
2 Dim rngMTMale As Range
3 Dim rngMTFemale As Range
4
5 Set rngMTMale = Range ( " MTMale " )
6 Set rngMTFemale = Range ( " MTFemale " )
7
8 Initialize rngMTMale := rngMTMale , rngMTFemale :=
rngMTFemale
9
10 Debug . Print Interpolate (0 , 0)
11 Debug . Print Interpolate (35.5 , 0)
12 Debug . Print Interpolate (35.5 , 1)
13 Debug . Print Interpolate (99.1 , 0)
14 End Sub
In the above test procedure, we used two named ranges defined in Table
10.1. We calculated the survival probabilities for a male aged 0, 35.5, and
99.1. We also calculated the survival probability for a female aged 35.5.
Executing the above sub procedure gives the following output:
1 0.99239
2 0.961165379370226
3 0.979048981352752
4 6.21200647276638 E -03
From the output, we see that a female aged 35.5 has a higher survival prob-
ability than a male of the same age.
Implementation 209
Exercise 10.3. Write a sub procedure named Testtpx to test the function
tpx by calculating the following probabilities: 1 p 35 and 3.6 p 35.2 for a male,
and 3.6 p 35.2 for a female.
In Lines 3–14 of the above code, we declared some variables to hold the
parameters of the contract. In Lines 15–16, we declared two variables to hold
the references to the range of forward rates and the worksheet of scenarios.
In Line 18, we defined a constant for the time step. In this program, we only
consider monthly time steps. In Lines 19–22, we defined some arrays to
hold the survival probabilities, the probabilities of dying within a period,
the discount factors, and the survivorship. In Lines 24–27, we declared some
variables to hold temporary values. The names of the variables match those
used in the formulas described in Section 10.3.
Valuing a GMDB requires two sets of information: the contract and the
scenarios. Hence, we implement two procedures to initialize the module-
level variables. We first implement the contract initialization procedure as
follows:
1 Sub InitializeContr ac t ( _
2 ByVal Gender As Integer , _
3 ByVal DOB As Date , _
4 ByVal Premium As Double , _
5 ByVal MEFee As Double , _
6 ByVal FundFee As Double , _
7 ByVal IE As Double , _
8 ByVal OE As Double , _
9 ByVal Term As Integer , _
10 ByVal Feature As String , _
11 ByVal LapseRate As Double , _
12 ByVal ValDate As Date , _
13 ByVal NumSce As Integer )
14 iGender = Gender
Implementation 211
15 datDOB = DOB
16 dPremium = Premium
17 dMEFee = MEFee
18 dFundFee = FundFee
19 dIE = IE
20 dOE = OE
21 iTerm = Term
22 sFeature = Feature
23 dLapseRate = LapseRate
24 datValDate = ValDate
25 iNumSce = NumSce
26
27 Dim dx As Double
28 dx = DateDiff ( " d " , datDOB , datValDate ) / 365#
29 Dim i As Integer
30 ap (0) = 1
31 aL (0) = 1
32 For i = 1 To 360
33 ap ( i ) = MLifeTable . tpx ( dx , i * dTimeStep ,
iGender )
34 aq ( i ) = ap ( i - 1) * MLifeTable . tqx ( dx + ( i -
1) * dTimeStep , dTimeStep , iGender )
35 If i < iTerm * 12 Then
36 aL ( i ) = aL ( i - 1) * (1 - dTimeStep *
dLapseRate )
37 Else
38 aL ( i ) = 0
39 End If
40 Next i
41 End Sub
This sub procedure requires only two arguments: the range of forward rates
and the worksheet of scenarios. Since the arguments are objects, we pass
the arguments by reference. In this sub procedure, we also calculate the
discount factors of all periods that can be used later.
Once we have all the contract information and the scenarios, we can
project the cash flows of the guarantee and calculate the present values. We
first implement a function to project the cash flows along a single path. We
implement this function as follows:
1 Function Project ( ByVal dShock As Double , ByVal ind As
Integer ) As Double
2 Dim i As Integer
3
4 aA (0) = (1 + dShock ) * ( dPremium * (1 - dIE ) * (1
- dMEFee ) - dOE )
5 aRC (0) = dPremium * (1 - dIE ) * dMEFee
6 aGD (0) = dPremium
7
8 For i = 1 To 360
9 If i Mod 12 = 0 Then
10 aA ( i ) = aA ( i - 1) * shtSce . Cells ( ind , i ) .
Value * _
11 (1 - dTimeStep * dFundFee ) * (1 -
dMEFee ) - dOE
12 aRC ( i ) = aA ( i - 1) * shtSce . Cells ( ind , i )
. Value * _
13 (1 - dTimeStep * dFundFee ) * dMEFee
14 If StrComp ( sFeature , " RP " , vbTextCompare )
= 0 Then
15 aGD ( i ) = aGD ( i - 1)
16 Else
17 aGD ( i ) = Worksh eetFunct ion . Max ( aGD ( i
- 1) , aA ( i ) )
Implementation 213
18 End If
19 Else
20 aA ( i ) = aA ( i - 1) * shtSce . Cells ( ind , i ) .
Value * _
21 (1 - dTimeStep * dFundFee )
22 aRC ( i ) = 0
23 aGD ( i ) = aGD ( i - 1)
24 End If
25 aDB ( i ) = Worksh eetFunct ion . Max (0 , aGD ( i ) - aA
(i))
26 Next i
27
28 Dim dPVBenefit As Double
29 Dim dPVCharge As Double
30 For i = 1 To 360
31 dPVBenefit = dPVBenefit + aq ( i ) * aL ( i ) * aDB
( i ) * ad ( i )
32 dPVCharge = dPVCharge + ap ( i ) * aL ( i ) * aRC ( i
) * ad ( i )
33 Next i
34 Project = dPVBenefit - dPVCharge
35 End Function
Exercise 10.4. Write a sub procedure named TestProject to test the func-
tion Project by doing the following:
row contains an array, and the name of the array appears in the first
column.
In the above function, we calculate the average present value by calling the
function Project for the specified number of scenarios.
In the above function, we first initialize the module MLifeTable in Line 10.
Then, we initialize the contract information in the module MGMDB in Lines
11–25. In Line 27, we pass the worksheet containing the base scenarios and
the range containing the corresponding forward rates to the module MGMDB.
In Lines 28–30, we calculate the fair market values for different equity shocks.
In Lines 32–36, we calculate the fair market values for different interest rate
shocks. Finally we write the results to the worksheets in Lines 38–40.
If we use the inputs shown in Figure 10.1, we see that outputs shown
in the same figure. The fair market value of this GMDB rider is –$5368.11.
Since it is negative, the present value of the benefits is lower than that of
the fees. The dollar delta is –$8256.88, meaning that when the market goes
216 Valuing a GMDB
up 1%, the fair market value of the guarantee will decrease about –$82.5688.
The dollar rho is close to zero, meaning that the fair market value of the
GMDB guarantee is not sensitive to the yield curve changes.
10.6 Summary
Variable annuities are insurance products that contain guarantees. Since
these guarantees are complicated, closed-form formulas to calculate the
fair market values of these guarantees do not exist. Insurance companies
rely heavily on Monte Carlo simulation to calculate the fair market values of
guarantees. In this chapter, we implemented a simple Monte Carlo method
to calculate the fair market value of a GMDB.
An advantage of the Monte Carlo simulation method is that it is flexible
to value various guarantees. However, Monte Carlos simulation is time-
consuming, especially when used to value a large portfolio of variable an-
nuities. Metamodeling approaches have been proposed to speed up the
Monte Carlo simulation method. See (Gan, 2013), (Gan, 2015a), (Gan and
Lin, 2015), and (Gan and Lin, 2016) for details.
11
Connecting to Databases
Table 11.1: Range Names Used by the Variable Annuity Inforce Tool
Name Refers To
FileName =Main!$C$4
IssueYear =Main!$C$3
TableName =Main!$C$5
217
218 Connecting to Databases
Table 11.1 gives a list of range names used by the simple tool. Figure
11.2 shows the variable annuity inforce table in an Access® database named
“va.accdb.” The name of the table is “Inforce10k,” and the table contains
10,000 variable annuity contracts, which are issued in different time. These
variable annuity contracts are generated randomly by a Java program (Gan,
2015b).
jects (ADO). ADO consists a set of objects for accessing data sources. A
main advantage of ADO is that it allows application developers to write pro-
grams to access data without knowing how the database is implemented.
In particular, ADO allows us to do the following:
Figure 11.3: Checking the reference for Microsoft ActiveX Data Objects 2.8
Library
There are many other SQL statements (Forta, 2012; Tale, 2016). The SQL
statements mentioned above will be used in this chapter.
11.3 Implementation
We organize the VBA code of the inforce tool into two modules: the
MDatabase module and the MInterface module.
This sub procedure requires one argument, which is the name of the Access
database. In Line 3, we construct a connection string by specifying the
Provider, the Data Source, and the Persist Security Info. The code in Line
3 is a common syntax for version 2007 and later versions of Excel. We set
Persist Security Info to false so that security-sensitive information (e.g., the
password) will not be returned as part of the connection. In Line 6, we
create a new ADODB.Connection object and assign it to the module-level
variable. In Line 7, we assign the connection string to the object. In Line 8,
we open the connection.
To close the connection, we use the following sub procedure:
1 Sub Disconnect ()
2 dbConnection . Close
3 Set dbConnection = Nothing
4 End Sub
In Line 2 of the above code, we call the Close method to close the connec-
tion. In Line 3, we release the memory of the variable by setting the variable
to Nothing.
The following function is used to retrieve a list of tables stored in an
Access database:
1 Function ListTables ()
2 Dim dbRecordSet As ADODB . Recordset
3
4 Set dbRecordSet = dbConnection . OpenSchema (
adSchemaTables )
5
6 Dim vTmp As Variant
7 vTmp = dbRecordSet . GetRows
8
9 Dim nRow As Integer
10 nRow = UBound ( vTmp , 2)
11
12 Dim aTable () As String
13 ReDim aTable (0 To nRow )
14
15 Dim i As Integer
16 Dim j As Integer
17 For i = 0 To UBound ( vTmp , 1)
18 If StrComp ( dbRecordSet . Fields ( i ) . Name , "
TABLE_NAME " , vbTextCompare ) = 0 Then
19 For j = 0 To UBound ( vTmp , 2)
20 aTable ( j ) = vTmp (i , j )
Implementation 223
21 Next j
22 Exit For
23 End If
24 Next i
25
26 ListTables = aTable
27 End Function
In the above test procedure, we assume that the Access database va.accdb
is saved in the same folder as the workbook. In Line 5, we open the con-
nection by calling the procedure Connect. In Line 8, we call the function
ListTables to get the table names. In Line 10, we close the connection
by calling the procedure Disconnect. Executing the above test procedure
gives the following output:
1 Inforce10k
2 MSysA ccessSto rage
3 MSysACEs
224 Connecting to Databases
4 MS ys Co mpl ex Co lum ns
5 MSysIMEXColumns
6 MSysIMEXSpecs
7 MSysNameMap
8 MSysNavPaneGroupCategories
9 MSysN avPaneGr oups
10 MSysNavPaneGroupToObjects
From the output, we see that many names start with “MSys.” These tables
are Microsoft Access system tables, which contain general information
about various specifications. The first name “Inforce10k” is the name of the
variable annuity inforce table.
Before creating a table in a database, we need to make sure that the
table does not exist in the database. The following function can be used to
detect whether a table exists or not in the database:
1 Function HasTable ( ByVal TableName As String )
2 Dim vTables As Variant
3 Dim bRes As Boolean
4
5 vTables = ListTables
6 bRes = False
7 Dim i As Integer
8 For i = 0 To UBound ( vTables )
9 If StrComp ( vTables ( i ) , TableName ,
vbTextCompare ) = 0 Then
10 bRes = True
11 Exit For
12 End If
13 Next i
14
15 HasTable = bRes
16 End Function
In this function, we first call the function ListTables to get the names of
all tables in the database. Then we use a For-Next loop to check whether
the given table name matches one of the names in the database. To test this
function, we use the following sub procedure:
1 Sub TestHasTable ()
2 Dim sFileName As String
3
4 sFileName = ThisWorkbook . Path & " \ va . accdb "
5 Connect ( sFileName )
Implementation 225
6
7 Dim vRes As Variant
8 vRes = ListTables
9
10 Debug . Print HasTable ( " Inforce10k " )
11 Debug . Print HasTable ( " 2008 " )
12
13 Disconnect
14 End Sub
The output indicates that table “2008” does not exist in the database.
The following sub procedure is used to delete a table in the database:
1 Sub DeleteTable ( ByVal TableName As String )
2 Dim sSQL As String
3 Dim dbCmd As ADODB . Command
4
5 Set dbCmd = New ADODB . Command
6 sSQL = " DROP TABLE " & TableName
7
8 dbCmd . ActiveConnection = dbConnection
9 dbCmd . CommandText = sSQL
10
11 dbCmd . Execute
12
13 Set dbCmd = Nothing
14 End Sub
Exercise 11.1. Open the database va.accdb and manually create a ta-
ble named Table1 in the database. Then write a sub procedure named
TestDeleteTable to delete the table Table1 from the database.
h
226 Connecting to Databases
This sub procedure requires three arguments: a range of data, a table name,
and the name of the primary key. We assume that the first row of the range
contains the column names of the table. In Lines 8–26, we construct the SQL
query for creating the table. The data types are derived from the data in the
second row of the range. Here, we only consider three types of data: Double,
String, and Date. We also assume that the first column of the range is the
primary key, which is a special column designated to uniquely identify
all table records. In Lines 28–34, we create an ADODB.Command object to
execute query.
Exercise 11.2. Suppose that the range "O2:Q5" contains the following data:
In Line 2, we create the table in the database by calling the sub procedure
CreateTable. In Lines 4–10, we create and configure an ADODB.Recordset
object. In Line 12, we open the record set for appending new records. In
Lines 16–22, we use a loop to append the rows in the range to the record set.
Exercise 11.3. Continue from Exercise 11.2 and write a sub procedure
named TestExcelToAccess to save the range "O2:Q5" to a table named
Grade in the database.
h
17 Dim i As Integer
18 Dim j As Integer
19 iU1 = UBound ( vTmp , 1)
20 iU2 = UBound ( vTmp , 2)
21 ReDim aRes (0 To iU1 , 0 To iU2 + 1)
22 For j = 0 To iU1 - 1
23 aRes (j , 0) = dbRecordSet . Fields ( j ) . Name
24 Next j
25 For i = 0 To iU2 - 1
26 For j = 0 To iU1 - 1
27 aRes (j , i + 1) = vTmp (j , i )
28 Next j
29 Next i
30
31 dbRecordSet . Close
32 Set dbRecordSet = Nothing
33 Set dbCmd = Nothing
34
35 ExtractRecord = aRes
36 End Function
The function requires one argument, which is an SQL query. In Lines 2–12,
we create an ADODB.Recordset object and an ADODB.Command object to
retrieve data from the database. In Lines 22–29, we convert the record set to
a two-dimensional array.
Exercise 11.4. Suppose that the database va.accdb contains a table named
Grade with the data given in Exercise 11.2. Write a sub procedure named
TestExtractRecord to retrieve the students whose grade is B.
h
The sub procedure uses three named ranges defined in Table 11.1. In Line
10, the SQL function year is used to get the year part from a date. In Lines
21–25, we write the retrieved contracts to the worksheet “Cohort.”
If we click the first button, we see that output as shown in Figure 11.4.
From the output, we see that all the contracts have an issue date in 2009.
The macro assigned to the second button is given below:
1 Sub Button2_Click ()
2 Dim ws As Worksheet
3
4 Set ws = Sheets ( " Cohort " )
5
6 Dim nCol As Integer
7 Dim nRow As Integer
8 nCol = ws . Cells (1 , 1) . End ( xlToRight ) . Column
9 nRow = ws . Cells (1 , 1) . End ( xlDown ) . Row
10
11 Dim sTableName As String
12 sTableName = " Cohort " & Range ( " IssueYear " )
Implementation 231
Figure 11.4: Output in the worksheet when the first button is clicked
Figure 11.5: New table in the database when the second button is clicked
232 Connecting to Databases
In this sub procedure, we just write the table in the “Cohort” worksheet to
the database. The name of the table in the database is “Cohort” plus the
issue year. If we click the second button, the data in the worksheet “Cohort”
will be saved to a new table, as shown in Figure 11.5.
• Add a new parameter named Product Type to the input area of the
interface shown in Figure 11.1.
• Modify the macro assigned to the first button to retrieve the contracts
with specified issue year and product type.
How many contracts with “MB” guarantee have an issue date in 2009?
11.4 Summary
In this chapter, we introduced how to connect to a Microsoft Access
database in Excel VBA. In particular, we introduced how to retrieve data
from an Access database to Excel, how to create tables in Access, and how
to transport data in Excel to an Access database. To learn more about the
SQL, readers are referred to (Forta, 2012; Tale, 2016).
12
Object-Oriented Programming
12.1 Introduction
Object-oriented programming (OOP) is a programming paradigm intro-
duced in the 1980s (Urtis, 2015). Since its introduction, OOP has grown
in popularity and been supported in many other programming languages,
such as C++ (Gan, 2011). It is not surprising that today’s Visual Basic for
Applications (VBA) programming language also supports OPP.
A computer program developed using the OOP method consists of
individual objects, each of which has its own properties and methods. For
example, Excel® is an application developed using the OOP method. In
Section 2.1, we introduced the Excel® object model. For example, a range
is an object in Excel® and contains properties (e.g., value and text) and
methods (e.g., select and copy), as described in Section 2.5.
In OOP, the objects of a computer program interact with each other.
Each object models an aspect of the problem one tries to solve. In OOP, an
object is called an instance of a class, which can be thought as a template of
objects. For example, Range is a class in Excel® VBA, and Range("A1") is a
Range object. In Section 1.3, we introduced how to define a VBA class.
An object-oriented (OO) VBA program has the following characteristics
(Sintes, 2001; Gan, 2011):
233
234 Object-Oriented Programming
an object will not affect other objects as long as we do not change the
interface of the object.
(c) Reusable. The classes created for an OO program can be used in other
programs.
(d) Maintainable. Since changing an object does not affect other objects,
fixing one part of an OO program will not cause changes in other parts.
Unlike other OOP languages, such as C++ and Java, VBA does not support in-
heritance. In other words, we cannot define a class by inheriting properties
and methods from another class.
12.2 Objects
As we mentioned before, an OO program consists of objects, each of which
models an aspect of the problem. To develop an OO bootstrapping tool, we
need the following objects: dates, yield curves, holiday calendars, swaps,
and a bootstrapping procedure.
Since an object is created from a class, which serves as a template for
objects. According to our analysis above, we need classes for dates, yield
curves, holiday calendars, swaps, and the bootstrapping procedure. Since
the VBA has the Date data type, we do not need to implement our date class.
We also do not need to implement a holiday calendar class, because we do
not need to keep copies of a calendar object. A holiday calendar is global in
the sense that we only need one copy of the holiday calendar. As a result, we
can just implement the functions of a holiday calendar in a regular module
rather than in a class module. Note that the VBA does not keep separate
copies of data in a regular module. However, VBA keeps separate copies of
data stored in a class module.
12.3 Implementation
To develop the OO bootstrapping tool, we decide to implement three classes:
a yield curve class, a swap class, and a bootstrapper class. These classes al-
low us to create yield curve objects, swap objects, and bootstrapper objects,
respectively.
Implementation 235
In the above code, we declare four module-level variables, which are prop-
erties used to store the curve date, the yield curve day count convention,
dates, and discount factors at these dates.
The sub procedure used to initialize the module-level variables is im-
plemented as follows:
1 Sub Initialize ( ByVal CurveDate As Date , ByVal YCDCC
As String )
2 datCurveDate = CurveDate
3 sYCDCC = YCDCC
4
5 ReDim aDate (0 To 0)
6 ReDim aDF (0 To 0)
7
8 aDate (0) = CurveDate
9 aDF (0) = 1
10 End Sub
This sub procedure requires two arguments: the curve date and the yield
curve day count convention. The array aDate is initialized to contain one
element, which is the curve date. The array aDF is also initialized to contain
one element, which is the discount factor at the curve date.
Sometimes we need to make a copy of a CZeroCurve object. To do that,
we can use the Clone method implemented as follows:
1 Function Clone () As CZeroCurve
2 Dim cZC As CZeroCurve
3 Dim iU As Integer
4 Dim i As Integer
5
6 Set cZC = New CZeroCurve
7 cZC . Initialize CurveDate := datCurveDate , YCDCC :=
sYCDCC
236 Object-Oriented Programming
In Line 6 of the above code, we use the New function to create a new object.
In Lines 7–10, we copy the data of this object to the new object. This function
calls another function called PushBack to copy the dates and the discount
factors from this object to a new object called cZC. Note that we use Set
to assign the new object to the function name at the end of this function.
We have to use Set here because cZC is an object. This method is useful,
as it allows us to experiment with a copy of a CZeroCurve object without
affecting the object.
The PushBack method is implemented as follows:
1 Sub PushBack ( ByVal datIn As Date , ByVal dDF As Double
)
2 Dim iU As Integer
3 iU = UBound ( aDate )
4
5 If datIn <= aDate ( iU ) Then
6 Err . Raise Number :=2002 , Source := " CZeroCurve .
PushBack " , Description := " The input date is
ealier than the last date . "
7 End If
8
9 ReDim Preserve aDate (0 To iU + 1)
10 ReDim Preserve aDF (0 To iU + 1)
11 aDate ( iU + 1) = datIn
12 aDF ( iU + 1) = dDF
13 End Sub
The method PushBack is a sub procedure that inserts a date and a discount
factor to the end of the arrays aDate and aDF. This method takes two argu-
ments: a date and a real number. The method checks whether the input
date is equal to or before the last date in the array aDate. If the input date is
equal to or before the last date in the array, the method will raise an error.
This method ensures that the dates in the array are sorted in an increasing
order.
We implement the raw interpolation method described in Section 8.2
in the following method:
Implementation 237
This function takes one argument, which is a date, and returns the discount
factor at the input date. If the input date is older than the curve date, the
function will raise an error. If the input date matches one of the dates saved
in the array aDate, the function just returns the corresponding discount
factor saved in the array aDF. Otherwise, the function searches for two dates
in aDate that enclose the input date. If the input date is not enclosed in any
two dates in aDate, the function will use the last two dates in aDate. Then,
the function performs the raw interpolation based on the two dates and the
two corresponding discount factors.
The following function allows us to get the dates corresponding to the
discount factors:
1 Function Dates () As Variant
2 Dates = aDate
3 End Function
The method Dates returns the array aDate as a variant. This method allows
us to extract the dates saved in the variable aDate, which is private to a
CZeroCurve object.
The methods mentioned above allow us to perform necessary actions
on a CZeroCurve object. If needed, we can add more methods to the class.
The class CSwap contains only two methods, which are given in the
following code:
1 Sub Initialize ( ByVal FixFreq As String , ByVal FixDCC
As String , ByVal FltFreq As String , _
2 ByVal FltDCC As String , ByVal Calendar As String ,
ByVal BDC As String , _
3 ByVal SetDays As Integer , ByVal EffectiveDate As
Date , ByVal Tenor As String , _
4 ByVal Rate As Double )
5
6 sFixFreq = FixFreq
7 sFixDCC = FixDCC
8 sFltFreq = FltFreq
9 sFltDCC = FltDCC
10 sCalendar = Calendar
11 sBDC = BDC
12 iSetDays = SetDays
13 datEffective = EffectiveDate
14 sTenor = Tenor
15 dRate = Rate
16
17 Dim i As Integer
18 datSet = datEffective
19 For i = 1 To iSetDays
20 datSet = RollDate ( AddDays ( datSet , 1) , " Follow
" , sCalendar )
21 Next i
22
23 vFixPaymentDates = GenerateSchedule ( datSet ,
sFixFreq , sTenor , sCalendar , sBDC )
24 vFltPaymentDates = GenerateSchedule ( datSet ,
sFltFreq , sTenor , sCalendar , sBDC )
25 End Sub
26
27 Function NPV ( ByRef cZC As CZeroCurve ) As Double
28 Dim dFixPV As Double
29 Dim dFltPV As Double
30 Dim dDF As Double
31 Dim i As Integer
32 Dim dT As Double
33 Dim dFR As Double
34
35 For i = LBound ( vFixPaymentDates ) + 1 To UBound (
vFixPaymentDates )
Implementation 241
The properties of the class CBootstrapper are used to store the input
and output data. For example, the properties aTenor and aRate stores
the tenors and rates of the input swap rates. The properties sFixFreq and
sFixDCC represent the payment frequency and the day count convention of
the fixed leg. The properties sFltFreq and sFltDCC represent the payment
frequency and the day count convention of the floating leg. The proper-
ties sCalendar and sBDC hold the business calendar and the business day
count convention of the swap. The properties datCurveDate, iSetDays,
and sYCDCC store the curve date, the number of settlement days, and the
curve’s day count convention, respectively.
The remaining two properties are used to store some immediate and
output values. For example, the property datSet stores the settlement date
of the swaps. For convenience purposes, we save the settlement date into a
variable, which can be used later. The property cZC stores the yield curve
that is bootstrapped from the input data.
To initialize the properties, we implement the following method:
1 Sub Initialize ( ByVal FixFreq As String , ByVal FixDCC
As String , ByVal FltFreq As String , _
2 ByVal FltDCC As String , ByVal Calendar As String ,
ByVal BDC As String , _
3 ByVal SetDays As Integer , ByVal EffectiveDate As
Date , ByVal YCDCC As String , _
4 ByRef Tenors () As String , ByRef Rates () As Double
)
5 Dim iL As Integer
6 Dim iU As Integer
7 Dim i As Integer
8
9 iL = LBound ( Tenors )
10 iU = UBound ( Tenors )
11
12 ReDim aTenor ( iL To iU )
Implementation 243
13 ReDim aRate ( iL To iU )
14
15 For i = iL To iU
16 aTenor ( i ) = Tenors ( i )
17 aRate ( i ) = Rates ( i )
18 Next i
19
20 sFixFreq = FixFreq
21 sFixDCC = FixDCC
22 sFltFreq = FltFreq
23 sFltDCC = FltDCC
24 sCalendar = Calendar
25 sBDC = BDC
26 iSetDays = SetDays
27 datCurveDate = EffectiveDate
28 sYCDCC = YCDCC
29
30 datSet = EffectiveDate
31 For i = 1 To iSetDays
32 datSet = RollDate ( AddDays ( datSet , 1) , " Follow
" , sCalendar )
33 Next i
34
35 Set cZC = New CZeroCurve
36 cZC . Initialize CurveDate := datCurveDate , YCDCC :=
sYCDCC
37 End Sub
The method SolveRate bootstraps a discount factor from a given swap rate.
The new discount factor is added to the yield curve.
The following method bootstraps all discount factors:
1 Sub BuildCurve ()
2 Dim iL As Integer
3 Dim iH As Integer
4 Dim i As Integer
5 iL = LBound ( aTenor )
6 iH = UBound ( aTenor )
7
8 For i = iL To iH
9 SolveRate iInd := i
10 Next i
11 End Sub
22 iH = UBound ( aOutDates )
23
24 ReDim aOut ( iL To iH , 0 To 4) As Double
25 For i = iL To iH
26 aOut (i , 0) = CDbl ( aOutDates ( i ) )
27 aOut (i , 1) = cZC . LogLinear ( aOutDates ( i ) )
28 dT = TFrac ( datCurveDate , aOutDates ( i ) , sYCDCC
)
29 If dT = 0 Then
30 aOut (i , 2) = 0
31 Else
32 aOut (i , 2) = - Wor ksheetFu nction . Ln ( aOut (i
, 1) ) / dT
33 End If
34 If i < iH Then
35 aOut (i , 3) = ( cZC . LogLinear ( aOutDates ( i ) )
/ cZC . LogLinear ( aOutDates ( i + 1) ) -
1) / _
36 TFrac ( aOutDates ( i ) , aOutDates ( i + 1) ,
sYCDCC )
37 Else
38 aOut (i , 3) = aOut ( i - 1 , 3)
39 End If
40 aOut (i , 4) = TFrac ( datCurveDate , aOutDates ( i )
, sYCDCC )
41 Next i
42 CreateOutput = aOut
43 End Function
1 Sub Button1_Click ()
2 Dim vRes As Variant
3 Dim aTenor () As String
4 Dim aRate () As Double
5 Dim sFixFreq As String
6 Dim sFixDCC As String
7 Dim sFltFreq As String
8 Dim sFltDCC As String
9 Dim sCalendar As String
10 Dim sBDC As String
11 Dim datCurveDate As Date
12 Dim iSetDays As Integer
13 Dim sYCDCC As String
14 Dim sOutputType As String
15
16 Dim iNumRates As Integer
17 Dim i As Integer
18 Dim iRows As Integer
19 Dim j As Integer
20
21 Dim cBT As CBootstrapper
22
23 iNumRates = Range ( " Rate " ) . Rows . Count
24 ReDim aTenor (1 To iNumRates )
25 ReDim aRate (1 To iNumRates )
26 With Range ( " Rate " )
27 For i = 1 To iNumRates
28 aTenor ( i ) = . Cells (i , 1) . Value
29 aRate ( i ) = . Cells (i , 2) . Value / 100
30 Next i
31 End With
32 With Range ( " Param " )
33 sFixFreq = . Cells (1 , 1) . Value
34 sFixDCC = . Cells (2 , 1) . Value
35 sFltFreq = . Cells (3 , 1) . Value
36 sFltDCC = . Cells (4 , 1) . Value
37 sCalendar = . Cells (5 , 1) . Value
38 sBDC = . Cells (6 , 1) . Value
39 datCurveDate = . Cells (7 , 1) . Value
40 iSetDays = . Cells (8 , 1) . Value
41 sYCDCC = . Cells (9 , 1) . Value
42 sOutputType = . Cells (10 , 1) . Value
43 End With
44
248 Object-Oriented Programming
The VBA code in the module MInterface is the macro assigned to the
button. The sub procedure gets input data from the worksheet, calls other
functions to bootstrap the yield curve, and extracts the output data to the
worksheet. In Lines 45, a new object of the class CBootstrapper is created.
In Line 46, the object is initialized with data from the worksheet. In Lines 49–
50, the functions BuildCurve and CreateOutput are called to bootstrap
the yield curve and get the output, respectively.
The input and output of the OO bootstrapping tool are identical to those
of the bootstrapping tool implemented in Chapter 8. If we click the button
of this tool, we see the results shown in Figure 8.1.
Exercise 12.1. In the MInterface module, we see that the three functions
Initialize, BuildCurve, and CreateOutput are called in sequence. How-
ever, it is possible that some users may not call these functions in sequence.
Modify the VBA code of the class CBootstrapper by doing the following:
(a) Add two properties named bInitialized and bCurveBuilt and assign
false to them.
(b) Modify the function Initialize by assigning true to the variable
bInitalized at the end of this function
Summary 249
Exercise 12.2. In Exercise 12.1, you are asked to modify the code of the
class CBootstrapper to address the issue that the three functions have to
be called in sequence. However, users still need to call the three functions
explicitly. We can modify the code such that users only need to call the func-
tions Initialize and CreateOutput, which calls the other function auto-
matically, if necessary. Modify the VBA code of the class CBootstrapper
and the module MInterface as follows:
(a) Add two properties named bInitialized and bCurveBuilt and assign
false to them.
(b) Modify the function Initialize by assigning true to the variable
bInitalized at the end of this function.
(c) Modify the function BuildCurve by checking whether bInitialized
is true. If bInitialized is false, raise an error. Also assign true to the
variable bCurveBuilt at the end of this function.
(d) Modify the function CreateOutput by checking whether bCurveBuilt
is true. If bCurveBuilt is false, call the function BuildCurve.
(e) Modify the function Button1_Click in the module MInterface by re-
moving the function call cBT.BuildCurve.
12.4 Summary
In this chapter, we implemented a bootstrapping tool using OO techniques.
We implemented three classes, each of which models an aspect of the
bootstrapping algorithm. In particular, we implemented a yield curve class,
a swap class, and a bootstrapper class. The advantage of using classes is that
VBA keeps a copy of the data contained in an object. By using classes, we
can create multiple objects of a class to contain different copies of data. For
250 Object-Oriented Programming
251
252 Solutions to Selected Exercises
Solution 2.6. The first and second statements are true. The third state-
ment is false. If the Visual Basic code is part of an add-in, ThisWorkbook
won’t return the active workbook. In this case, ActiveWorkbook is the work-
book calling the add-in, whereas ThisWorkbook returns the add-in work-
book.
Solution 2.8. The Index property is read-only. We cannot assign a value
to it.
Solution 2.10. We can find out the address using the following code:
1 Sub FindAddress2 ()
2 Debug . Print Cells (20 , 51) . Address
3 End Sub
1 Sub W o r k s h e e t F u n c t i o n D e m o 3 ()
2 Debug . Print Works heetFunc tion . Pi * 2
3 Debug . Print Works heetFunc tion . NormDist (1.5 , 0 , 1 ,
True )
4 Debug . Print Works heetFunc tion . NormInv (0.025 , 0 ,
1)
5 End Sub
10 arM (2 , 2 , 1) = 5
11 arM (2 , 2 , 2) = 6
12
13 Debug . Print arM (1 , 1, 2) , arM (1 , 2, 1)
14 Debug . Print arM (1 , 2, 1) , arM (1 , 2, 2)
15 Debug . Print arM (2 , 2, 1) , arM (2 , 2, 2)
16 Debug . Print arM (2 , 2, 1) , arM (2 , 2, 2)
17 End Sub
1 Sub StringDemo11 ()
2 Dim strA As String
3 Dim strB As String
4 Dim intA As Integer
5 strA = " This car is beautiful . That car is small .
Your car is big . "
6
7 intA = InStr ( strA , " car " )
8 strB = Mid ( strA , 1 , intA ) & Replace ( strA , " car " ,
" house " , intA + 1 , 1)
9 Debug . Print strB
10 Debug . Print Len ( strA )
11 Debug . Print Len ( strB )
12 End Sub
12 IsMLK = False
13 End If
14 End Function
The output is
1 True
2 True
3 False
Solution 4.8. It is an infinite loop. Executing the code may freeze your
computer.
Solution 4.10. We can write the sub procedure as follows:
1 Sub IfThenDemo4 ()
2 Dim intDay As Integer
3 Dim intM As Integer
4 Dim intWeekDay As Integer
5
6 intM = Month ( Date )
7 intDay = Day ( Date )
8 intWeekDay = Weekday ( Date )
9
10 ' Labor Day is the first Monday in September
11 If intM = 9 And intDay < 8 And intWeekDay = 2
Then
12 Debug . Print " Today is Labor day "
13 Else
14 Debug . Print " Today is not Labor day "
15 End If
16 End Sub
Operators and Control Structures 259
3 Factorial = 1
4 Else
5 Factorial = n * Factorial ( n - 1)
6 End If
7 End Function
Solution 5.6. The first function always returns the same random num-
bers. However, the second function returns different random numbers for
different calls.
Solution 5.8. We can add event-handlers for the NewChart and New-
Sheet events as follows:
1 Private Sub Workbook _NewChar t ( ByVal Ch As Chart )
2 If ThisWorkbook . Sheets . Count >= 5 Then
3 Application . DisplayAlerts = False
4 Ch . Delete
5 Application . DisplayAlerts = True
6 End If
7 End Sub
8
9 Private Sub Workbook _NewShee t ( ByVal Sh As Object )
10 If Sheets . Count > 5 Then
11 Application . DisplayAlerts = False
12 Sh . Delete
13 Application . DisplayAlerts = True
14 End If
15 End Sub
Solution 5.10. We can add an event-handler for the Active event of the
worksheet as follows:
1 Private Sub Wo rk she et _A cti va te ()
2 MsgBox ActiveSheet . Name
3 End Sub
8 Dim lV As Long
9 Dim lC As Long
10 Dim lR As Long
11 Dim lS As Long
12
13 lN = 7 - ( lY + lY \ 4 - lY \ 100 + lY \ 400 - 1)
Mod 7
14 lG = 1 + lY Mod 19
15 lE = (11 * lG - 10) Mod 30
16 lH = lY \ 100
17 lSOL = lH - lH \ 4 - 12
18 lLUN = ( lH - 15 - ( lH - 17) \ 25) \ 3
19 lV = lE \ 24 - lE \ 25 + ( lG \ 12) * ( lE \ 25 -
lE \ 26)
20 lE = (11 * lG - 10) Mod 30 - ( lSOL - lLUN ) Mod 30
+ lV
21 If lE < 24 Then
22 lR = 45 - lE
23 Else
24 lR = 75 - lE
25 End If
26 lC = 1 + ( lR + 2) Mod 7
27 lS = lR + (7 + lN - lC ) Mod 7
28
29 EasterSunday = DateSerial ( lY , 3 , lS )
30 End Function
Bauer, D., Kling, A., and Russ, J. (2008). A universal pricing framework
for guaranteed minimum benefits in variable annuities. ASTIN Bulletin,
38(2):621–651.
Bovey, R., Wallentin, D., Bullen, S., and Green, J. (2009). Professional Excel
Development: The Definitive Guide to Developing Applications Using Mi-
crosoft Excel, VBA, and .NET. Addison-Wesley Professional, 2nd edition.
Brown, R. A., Campbell, T. A., and Gorski, L. M. (2002). Valuation and capital
requirements for guaranteed benefits in variable annuities. Record, 28(3).
Cathcart, M. J., Lok, H. Y., McNeil, A. J., and Morrison, S. (2015). Calculating
variable annuity liability “greeks” using Monte Carlo simulation. ASTIN
Bulletin, 45(2):239–266.
275
276 References
Gan, G. (2015b). A multi-asset Monte Carlo simulation model for the val-
uation of variable annuities. In Proceedings of the Winter Simulation
Conference, pages 3162–3163.
Gan, G., Ma, C., and Xie, H. (2014). Measure, Probability, and Mathematical
Finance: A Problem-Oriented Approach. John Wiley & Sons, Inc., Hoboken,
NJ.
Press, W., Teukolsky, S., Vetterling, W., and Flannery, B. (2002). Numerical
Recipes in C++: The Art of Scientific Computing. Cambridge University
Press, 2nd edition.
Roman, S. (2002). Writing Excel Macros with VBA. O’Reilly Media, 2nd
edition.
Tale, S. (2016). SQL: The Ultimate Beginners Guide: Learn SQL Today. Cre-
ateSpace Independent Publishing Platform.
B E
basis point, 199 Easter Sunday, 158
bisection method, 180 economic scenario generator, 184
bps, 205 Excel macro recorder, 13
bug, 115
business day convention, 134 F
fixed-length string, 57
C following, 134
calendar, 131 forward rate, 163
class, 11, 233 function procedure, 9, 95
class module, 10
collection, 17 G
comment, 15 GMAB, 200
compilation error, 109 GMDB, 200
connection string, 222 GMLB, 200
constant, 54 GMMB, 200
continous return, 185 GMWB, 200
cumulative density function, 186 Greek, 205
Gregorian calendar, 131
D guarantee, 200
database, 218
278
Index 279
W
Watch window, 115
Y
yield curve, 162
Z
zero-coupon bond, 162
zero-coupon rate, 162
Index of VBA Keywords
Symbols Byte, 45
*, 75 ByVal, 124
+, 75
-, 75 C
/, 75 Calculate, 30
;, 88 Case, 81, 85
=, 79 Case Else, 86
#, 77, 146 CDbl, 77
&, 78 Ceiling, 38
ˆ, 75 Cells, 28, 33
>, 79 Charts, 25, 28
>=, 79 Clear, 36
<, 79 ClearContents, 20
<=, 79 Close, 26, 27, 31
Column, 35
A Columns, 20, 21, 28
Activate, 20, 26, 30 Command, 219
ActiveCell, 21 CommandText, 225
ActiveChart, 25 Connection, 219
ActiveConnection, 225 Const, 54
ActiveSheet, 21, 25 Copy, 20, 31, 36
ActiveWorkbook, 21 Count, 27, 35, 38
Add, 27, 31 CountIf, 38
Address, 35 CREATE TABLE, 221
ADODB.Connection, 221 CStr, 78
ADODB.Recordset, 223 Currency, 45
And, 80
Application, 17, 21 D
Date, 45, 66, 84
B DateAdd, 66
Boolean, 45 DateDiff, 66
ByRef, 124, 153 DatePart, 66
281
282 Index of VBA Keywords
H N
Hour, 66 Name, 25
Index of VBA Keywords 283
VarType, 47, 86
vbBlue, 36
vbWhite, 36
W
WeekDay, 66, 84
WeekDayName, 66
WHERE, 220
While, 87
Workbook, 17, 23
Workbooks, 17
Worksheet, 17, 28, 32
WorkSheetFunction, 17
WorksheetFunction, 37
Worksheets, 17, 25
X
xlPasteAll, 36
Xor, 80
Y
Year, 66
year, 230