目 录
6前言
7致谢
8第1章 介绍
金融学概览
收益分布假设
数学和统计方法
数值方法
Excel 解决方案
本书主题
有关Excel工作簿
意见和建议
12第2章 高级Excel函数和过程
访问Excel函数
数学类函数
统计类函数
使用频率函数Frequency
使用分位数函数Quartile
使用正态函数Norm
查找类函数
其他类型函数
审核工具
模拟运算表(Data Tables)
建立单变量模拟运算表
建立双变量模拟运算表
XY图
访问数据分析和规划求解
使用区域名称
回归分析
单变量求解
矩阵代数以及相关函数
矩阵介绍
矩阵转置
矩阵相加
矩阵相乘
矩阵求逆
线性方程组求解
Excel矩阵函数小结
35小结
37第3章 VBA介绍
掌握VBA的好处
VBA的面向对象观点
编写VBA宏
简单VBA子程序
交互函数MsgBox
编写环境
输入代码并运行宏
录制按键和编辑代码
编程要素
变量和数据类型
VBA数组变量
控制结构
控制重复过程
在代码中使用Excel和VBA函数
编程的一般观点
宏与电子表格之间的通信
子程序实例
图表
正态概率散点图
用规划求解产生有效边界
61小结
62附录3A Visual Basic编辑器
65附录3B 用‘相对引用’模式来录制按键
67第4章 编写VBA用户定义函数
简单销售佣金函数
在工作表中创建Commission(Sales)函数
多参数期权定价函数
在VBA中操作数组
数组变量的期望和方差函数
数组变量的组合方差函数
输出数组形式的函数
在用户定义函数中调用Excel和VBA函数
在用户定义函数中使用VBA函数
加载宏
编写VBA函数的优缺点
80小结
80附录4A 演示函数如何处理数组
82附录4B 二叉树期权定价函数
87编写函数练习
91第5章 股票的有关简介
92第6章 投资组合最优化
组合的均值和方差
组合的风险-收益表示
用规划求解得到有效点
求有效边界(黄和利曾伯格的方法)
有约束边界组合
无风险资产和风险资产的结合
问题一 一种无风险资产和一种风险资产的组合
问题二 存在两种风险资产的组合
问题三 一种无风险资产和一个风险投资组合
Module1中的用户定义函数
Module1中用于解决三类常见组合问题的函数
模块M中的宏功能
108小结
109第7章 资产定价
单因素模型
估计β系数
资本资产定价模型(CAPM)
方差-协方差矩阵
风险值(VaR)
水平财富
正态和对数正态分布矩之间的关系
Module1中的用户定义函数
119小结
120第8章 投资组合业绩评价
传统业绩评价方法
主动—被动管理
风格分析(Style Analysis)
简单风格分析
滚动时段风格分析
风格权重的置信区间
Module1中的用户定义函数
131小结
133第9章 股票期权介绍
布莱克-舒尔斯公式的起源
布莱克-舒尔斯公式
对冲投资组合(Hedge Portfolios)
风险中性定价
风险中性定价的单期二叉树模型
期权平价关系(Put-Call Parity)
红利(Dividends)
美式期权的特征
数值方法
波动率和非正态股票收益
140小结
142第10章 二叉树
二叉树介绍
简化的二叉树
JR二叉树
CRR树
二项分布近似与布莱克-舒尔斯公式
CRR二叉树的收敛性
LR树
CRR树与LR树的比较
美式期权和CRR美式二叉树
Module0和Module1中的用户定义函数
155小结
157第11章 布莱克-舒尔斯公式
布莱克-舒尔斯公式
在Excel中运用布莱克-舒尔斯公式
外汇(Currencies)和商品(Commodities)期权
计算期权的‘希腊’参数
对冲组合
布莱克-舒尔斯公式的正式推导
Module1中的用户定义函数
166小结
168第12章 欧式期权定价的其它数值方法
蒙特卡罗模拟介绍
对偶变量(Antithetic Variables)模拟
准随机抽样(Quasi-Random Sampling)模拟
模拟方法比较
蒙特卡罗 模拟中的希腊参数计算
数值积分
Module1中的用户定义函数
176小结
177第13章 非正态分布和隐含波动率
非正态分布假设下的布莱克-舒尔斯 公式
隐含波动率(Implied Volatility)
调整偏度(Skewness)和峰度(Kurtosis)
波动率曲线(The Volatility Smile)
Module1中的用户定义函数
185小结
187第14章 债券期权定价介绍
利率期限结构
附息债券的现金流和到期收益率
二叉树
布莱克的债券期权定价公式
久期和凸性
符号
192小结
193第15章 利率模型
Vasicek利率期限结构模型
Vasicek模型对零息票债券欧式期权定价
Vasicek模型对附息债券欧式期权定价
CIR利率期限结构模型
CIR模型对零息票债券欧式期权定价
CIR模型附息债券欧式期权定价
Module 1中的用户定义函数
200小结
202第16章 拟合利率期限结构
对数正态分布利率树
正态利率二叉树
BDT树
用BDT树为债券期权定价
Module 1中的用户定义函数
209小结
210附录 其它VBA函数
210预测
211ARIMA模型
212样条
213特征值和特征向量
前言
当被问到为什么要攀登珠穆朗玛峰时,登山员通常会说:“因为它在那儿。”而我们写《高级金融建模》这本书则出于相反的原因。无论是以前还是现在,几乎没有一本书重点突出和解释VBA函数在Excel中的应用。另一方面,能够掌握金融领域数值方法精髓的书也寥寥无几。
有人认为,像Excel这样的电子制表软件,不能满足高级技术和数值分析领域(如金融衍生工具的定价)的需要,现在这种想法已经过时。以前通过专门的软件包和语言进行的计算,现在可以应用有效的代码和VBA函数,在一台普通的电脑上只需一秒就可以完成。通过使用Excel和VBA编码,可以使得以前处于黑箱中的计算过程明朗化。
最初,宏的出现拓宽了Excel的应用范围,后来这一应用促进了VBA语言在Excel中的全面发展,从股票计算、期权计算,最后到债券计算,VBA广泛应用于金融领域中的各种计算。在本书中,可以学习到一些新的Excel技巧,并可更深入地理解数值方法在金融中的应用。
本书的基础部分来源于伦敦商学院的MBA选修课程讲义《基于计算机的金融建模》。书中的股票部分是学习《资产组合管理》课程的基础,该课程每年在日内瓦的国际货币银行中心举办一次。而关于期权和债券的章节则来自城市大学商学院计算机硕士课程《数值方法》。本书适用于研究生和本科高年级学生。
使用本书时,读者必须采取积极尝试的态度,学会提出问题并解决问题,既要理解书中的代码和VBA用户定义函数,也要勇于在实践中应用它们。由于假设资产收益服从对数正态分布,并将二叉树作为一种核心数值方法,因此我们的解释可以建立在概率和统计中常用的结论基础上。全书采用了统一的符号,并且用图片显示了Excel和VBA的应用过程,这些都有助于读者更好地理解本书内容。
致谢
本书得益于之前的学术研究者和金融研究机构,他们发展了有关金融理论,并提出了相应的数值方法,从而形成了本书的基本内容。用牛顿的话来说,“如果我看得更远,那是因为我站在巨人的肩膀上”。
感谢伦敦商学院和城市大学商学院的同事,特别是Elroy Dinenis,保罗·马什和Kiriakos Vlahos。
同时,还感谢萨姆·惠特克对我们的热心鼓励,作为一位编辑,他付出了很大的努力和耐心。
最后,感谢家人和朋友对我们的耐心,因为本书酝酿了较长一段时间,这期间给他们添了不少麻烦。
第1章 介绍
我们希望《高级金融建模》一书可以证明,能够应用电子制表软件成功地实现大部分的金融模型。这些模型从二十世纪五十年代早期发展到九十年代末期,覆盖了整个金融领域,包括股票、股票期权和债券期权。只要辅助使用VBA语言,这些模型完全能在Excel电子表格中实现。而用户定义函数提供了一个方便的程序库,使得计算的速度和准确度大大提高。
《高级金融建模》应该看作是这个领域中传统教材的补充读物(它甚至是对传统教材的纠正)。本书没有列出金融模型的详细推导过程,目的是为了能够涵盖更多的模型和方法,特别是将重点放在更新的研究成果上。
金融领域发展的重要理论包括:二十世纪五十年代的组合理论,六十年代的资本资产定价模型(CAPM),以及七十年代的布莱克-舒尔斯公式,这些理论中的解析解现在都能直接计算。这都得益于最近一二十年来发展的数值算法。通过选择适当的参数,二叉树方法在股票和债券期权定价的数值算法中扮演着重要的角色。在最近几年,金融领域的研究重点落在寻找有效的计算方法上,而不是理论本身。
尽管本书覆盖了大部分的金融领域,并且包括了不少复杂的模型,但只需应用Excel,以及Excel中内嵌的函数和VBA程序,就能完美地解决问题。这使得我们可以将常用的假设(对数正态分布)、数学问题(期望)和数值方法(二叉树)在金融建模领域统一起来。当然,我们也努力确保本书使用一致和简单的符号,以便表达的更加清晰。
尝试在本书中覆盖大部分的金融研究课题,这对我们来说既是一个挑战也是一个机遇。机遇就是我们可以纵览金融领域,并将资产定价中的假设、数学问题、数值方法和Excel的解法连接起来,总结出一般性规律。在以下的几节中,将简要地描述在股票、期权和债券计算中,关于金融、数学、数值方法和Excel特点方面的一些问题。以下的内容将会在以后的章节中详细地分析。
金融学概览
现代金融学作为一门学科与经济学分离,起源于1952年马可维茨创立的组合理论。马可维茨利用效用理论对个人投资者的选择进行建模,并且建立“均值-方差”方法来检验收益(以资产的平均收益来度量)和风险(以资产收益的方差来度量)之间的关系。这一研究成果后来导致了夏普,林特恩和特雷纳的资本资产定价模型(CAPM)的发展。CAPM是一个均衡模型,它描述了股票的期望收益。模型中引入beta作为测量可分散风险的因子,并证明构建股票组合能够有效地减少个别风险事件带来的总体风险。
另外一个重要的理论就是布莱克和舒尔斯的股票期权定价公式,这个公式是构筑在对冲组合(无风险)的基础上的。同时,默顿对布莱克-舒尔斯公式进行了扩展,使其适用于连续股利的情况,并可对商品期货期权和外汇期权定价。公式最初的推导需要解物理学中常见的扩散方程,但用风险中性方法也可以推导出来。
收益分布假设
尽管组合理论是根据个人投资者的选择推导出来的,但是它也可以通过对资产价格收益的分布进行合理假设来推导。标准的假设就是股票收益服从对数正态分布,或者假设股票的对数收益服从正态分布。最近,业界学者检验了实际分布同严格正态分布之间的偏离效应(偏度和峰度),并建议使用一些其它的分布(如逆gamma分布)。
而债券与股票相比有许多不同之处,因此债券期权定价的出发点是短期利率。一般假设短期利率服从对数正态分布或正态分布。这些概率分布的特性被广泛应用于各种金融研究中。
数学和统计方法
在关于股票的章节中,涉及到最优化数学方法。这些最优化方法可能含有约束条件,如夏普基于资产收益所进行的分析。在他的分析中,代表线性回归的斜率。
期权定价是在风险中性的条件下求统计学中的数学期望。对数股票价格的正态分布可以用离散的二项分布来近似。二项分布为计算期权的期望价格提供了一个框架。
数值方法
在关于组合最优化的章节中,最优化涉及到组合的方差,而解决最优化的数值方法是二次规划。风格分析也用到了二次规划,也就是使得误差的方差最小。而线性回归也是通过选择斜率系数来使误差项的平方和最小,尽管它通常不被看作是最优化问题。与一般最优化问题有所不同的是,线性回归为计算系数提供了一个直接公式。
在为期权定价方面,二叉树方法为计算风险中性期望提供了一个分析框架。我们通过检验三个不同二叉树的收敛效应来强调参数选择的重要性。这些二叉树也可以给美式期权定价,在美式期权中,期权可以在到期日之前的任意时刻执行。
在欧式期权中,像蒙特卡罗模拟和数值积分等技巧也经常用到。而数值迭代方法,特别是牛顿-拉夫森方法,可以用来估算期权市场价格中的隐含波动率。
Excel 解决方案
电子表格展示了如何应用Excel进行建模。在每张工作表中,所有单元格中的公式都很容易计算,而我们也尽量对单元格中的中间计算过程进行合并。电子表格具有灵活的特点,当参数改变时,结果也随之发生变化,这方便使用者检验参数对计算结果的影响。
书中所有的模型和方法都会实现两次:一次通过电子表格,另一次通过VBA函数。这样做的目的是检验数值计算的精确度。
部分VBA程序是宏,这通常被视为VBA在Excel中的主要应用。但绝大部分计算程序都是用户定义函数。我们会展示这些函数用VBA语言编写是如何的简单,并说明它们如何与Excel的内嵌函数结合在一起,包括功能强大的矩阵函数。
Excel中的单变量求解(Goal Seek)和规划求解(Solver)是用来解决最优化问题的。我们会展示这些方法如何在VBA用户定义函数和宏中自动实现。Excel的另外一个未被充分利用的功能是数组函数(用Ctrl+Shift+Enter组合键来调用),我们会在用户定义函数中使用它们。为了提高效率,在用户定义函数中使用的二叉树只采用了一维数组(向量)而不是二维数组(矩阵)。
本书主题
本书包括四部分,第一部分介绍用Excel进行高级建模的特点,其后三部分是其在金融领域的应用。应用的三部分内容分别涉及股票、股票期权和债券期权。
第2章介绍本书需要用到的高级Excel函数和技巧。重点关注Excel中的数组函数,并用较短的篇幅介绍矩阵运算的相关知识。
第3章介绍VBA编程环境和一种循序渐进地编写VBA子程序(宏)的方法。并用例子说明宏是如何自动操作和重复Excel中的任务的。
第4章介绍VBA用户定义函数,它在金融计算中至关重要。强调如何处理标量变量和数组变量,包括将它们作为VBA函数的输入变量和输出变量。另外,用循序渐进的方法列举了一些例子。特别地,通过写用户定义函数为欧式期权(布莱克-舒尔斯公式)和美式期权定价(二叉树)。
第5章介绍第一个应用部分——如何处理股票。
第6章讲解组合最优化,利用规划求解和分析解。规划求解经常用于电子表格计算,并能在VBA宏中自动执行,因此在本章的其他部分也会频繁出现。通过采用Excel和VBA中的数组函数,我们演示了如何得到资产组合有效边界上的点。组合理论的发展衍生分为三个常见问题,它们将在后面的章节中介绍。
第7章转入资产定价,从单因子模型出发,介绍资本资产定价模型(CAPM),最后讨论风险值(VaR)。本章的另一个主题是关于资产对数收益服从正态分布的假设。
第8章的主要内容是各种模型的效果测定,从最早使用的单参数测量到目前最具实用性的多因子模型(如风格分析)。在本书中,我们第一次说明在风格分析中如何确定资产权重的置信区间。
第9章介绍第二个应用部分,即如何处理股票期权。在股票对数收益服从正态分布的假设下,我们演示了构建对冲组合在布莱克-舒尔斯期权定价公式中的重要地位。并具体解释了期权价值是风险中性条件下期权未来收入期望值的折现值。
第10章介绍二叉树,它被看作是对数股票价格服从的连续正态分布在离散情况下的近似。实际应用时,由于二叉树方法能够有效地处理美式期权的定价问题,因此它成为期权定价数值方法的核心。本章为二叉树方法列举了三套不同的参数选择,其中包括LR树。与标准的二叉树相比,它拥有更好的收敛性和准确度。并利用一个九期树作例子,用户定义函数能够处理任何期数的二叉树定价。
第11章回到布莱克-舒尔斯公式,并演示了它的适应性(能够对外汇和商品期货期权定价)和它对资产价格假设的依赖性。
第12章介绍计算布莱克-舒尔斯公式中期望值的两种方法。这两种方法分别是蒙特卡罗模拟法和数值积分法。尽管对简单期权来说,这两种方法并没有多大的优势,但在为复杂的期权定价时,它们扮演着重要角色。
第13章放开关于资产对数收益服从正态分布的假设,介绍在背离原来假设的基础上(主要通过改变偏度和峰度参数),由期权市场价格确定的隐含波动率曲线(volatility smile)。本章还介绍了计算欧式期权价格隐含波动率的有效方法。
第14章为第三个应用部分,即如何处理债券期权。由于债券与股票相比存在许多不同的属性,因此为债券期权定价时出现的数学问题和使用的数值方法也有所不同。我们根据一系列零息票债券价格来定义期限结构,并展示用短期利率如何构建二叉树模型,这个模型可以为零息票债券现金流定价。
第15章涵盖了两个利率模型,Vasicek模型以及考克斯,英格索尔和罗斯模型(CIR模型)。我们分析了零息票债券价格和零息票债券期权的解析解,并介绍了为附息债券期权定价的一种方法。
第16章介绍了在给定零息票债券期限结构的情况下,如何用短期利率构建二叉树模型。构造著名的布莱克-德曼-托伊利率树模型(用电子表格和用户定义函数),并展示它如何为欧式和美式零息票债券期权定价。
附录中是其它用户定义函数,这些函数与我们选定的几个应用部分的联系不很密切。但它们是有用的工具箱,可以作为ARIMA模型、样条、特征值和其它计算过程的函数。
有关Excel工作簿
第一部分集中介绍Excel函数和理解VBA语言。这部分有三个相关工作簿,AMFEXCEL,VBSUB和VBFNS分别对应于第2,3和4章。
第二部分是关于股票的三个工作簿,EQUTY1,EQUTY2和EQUTY3,分别对应第6,7和8章。
第三部分关于股票期权有四个工作簿,OPTION1,OPTION2,OPTION3和OPTION4,分别对应第10,11,12和13章。
第四部分关于债券方面有两个工作簿,BOND1和BOND2,对应第14,15和16章,具体见书中的解释。
附录有一个工作簿OTHERFNS。
意见和建议
尽管花费了很大精力收集材料和撰写本书,我们仍然很乐意接受意见、建议甚至是改正和改善。请发到电子邮箱mstaunton@或者通过以下网页与我们联系。
第2章 高级Excel函数和过程
本章回顾了本书用到的一些函数和过程。包括Excel中各类函数中的数学、统计和查找函数,以及常用过程,如建立模拟运算表(Data Tables)和用XY图显示结果等。还包括汇总数据集、进行回归分析以及访问Excel单变量求解(Goal Seek)和规划求解(Solver)的方法。目的是为了阐明和保证这些内容能扫清读者前面的障碍。高级Excel用户可以略过这些内容,或在需要的时候再来参考本章的内容。为了使这些不同的主题更有趣和更具有交互性,本文提供了一个包含本章全部例程的工作簿,可以用来检测读者的熟练程度。
访问Excel函数
Excel提供了许多工作表函数,它们是一些已经编写好的计算程序。函数常用于电子表格的简单计算,在VBA宏代码和用户定义函数中也经常用到这些基本函数(见第三章和第四章)。
点击标准工具栏中的粘贴函数按钮(标记为fx)就可以访问这些函数。函数向导如图所示,函数分为几个不同的类别:如数学与三角函数类、统计类、逻辑类、查找与引用类,等等。
【参照书中第9页的图】
图 粘贴函数对话框显示数学与三角函数类别中的COMBIN函数
如图所示,数学与三角函数类别中的COMBIN函数被选中,这时对话框下面出现该函数输入值和输出值的简单描述。要想得到更详细的描述,可以点击帮助按钮(标记为?)。
点击确定按钮之后,就会出现提供适当参数输入框的公式面板,如图所示。需要输入的信息可以用键盘键入(如这里),也可以通过选择电子表格中的网格来引用(点击输入框右侧的按钮可以缩小公式面板)。注意,可以拖动公式面板离开它原来的位置。点击面板上的确定按钮或编辑栏中的勾号,就可以把公式输入到电子表格。
【参照书中第10页的图】
图 在公式面板中建立COMBIN函数
图显示,在公式面板里输入COMBIN函数参数的时候,编辑栏中会相应地出单元格公式的基本结构,而且粘贴函数按钮会呈现出‘按下’状态。还应该注意的是,粘贴名称按钮(标记为 =ab)可以将已命名的单元格粘贴到公式中。(为单元格区域以及引用单元格区域命名的内容见节。)
不仅可以访问Excel自带的函数,粘贴函数按钮还可以访问用户定义函数,见第四章内容。
讨论完如何访问函数后,接下来我们介绍一些常用的数学和统计类函数。
数学类函数
本书用到的数学与三角函数有:EXP(x)、LN(x)、SQRT(x)、RAND()、FACT(x)和COMBIN(number,number_chosen)。
EXP(x)返回指数函数的值,exp(x)或。例如:
EXP(1)返回e的值(,小数位数为4)
EXP(2)返回的值(,小数位数为4)
EXP(-1)返回1/e或的值(,小数位数为5)
在金融计算时,经常需要利用复利(或折现)因子将不同时段的现金流转换为未来价值(或现值)。给定连续复利r,则一年的复利因子为exp(r),对应的年利率为,如果复利以年为基础,则公式为:
关于连续复利以及EXP函数的应用,将在节的模拟运算表中作进一步的阐述。
LN(x)返回x的自然对数值。注意,x必须为正,否则函数会因数值溢出而返回#NUM!。例如:
LN()的返回值为-1
LN()的返回值为1
LN()的返回值为2
LN(-4)的返回值为#NUM!
在金融领域,我们经常与(自然)对数收益打交道,可以利用LN函数将收益值转换为对数收益。
SQRT(x)返回x的平方根。很显然,x必须为非负,否则函数会因数值溢出而返回#NUM!。
RAND()产生[0, 1]区间均匀分布的随机数。每次电子表格重新计算时,产生的随机数都不一样。用蒙特卡罗模拟法计算期权价格时,我们可以利用RAND()函数来产生随机数。
FACT(number)返回整数number的阶乘,它等于1×2×3×…×number。例如:
FACT(6)的返回值为720
COMBIN(number,number_chosen)返回number个元素的组合值(子集大小为number_chosen),子集可以按任何顺序组合。例如,如果某支股票的价格在四个离散时间里要么上涨,要么下跌,则出现三次上涨(和一次下跌)序列的个数为:
COMBIN(4,1) = 4 或者COMBIN(4,3) = 4
也就是这样四个序列‘上涨-上涨-上涨-下跌’、‘上涨-上涨-下跌-上涨’、‘上涨-下跌-上涨-上涨’和‘下跌-上涨-上涨-上涨’。从统计的角度来说,COMBIN(4,3)表示从4个元素中选择3个元素的组合值,通常记为(或者通用的)。
Excel中还有一些函数可以进行矩阵转置、矩阵相乘、或求方阵的逆。相应的函数分别为:
TRANSPOSE(array)返回矩阵array的转置
MMULT(array1,array2)返回两个矩阵的乘积
MINVERSE(array)返回矩阵array的逆矩阵
这些函数都属于数学类函数。可能有些读者对矩阵并不熟悉,为了熟悉这些函数,我们将在本章的末尾对矩阵做介绍(见)。
统计类函数
Excel中有一些函数可以快速汇总数据集(Excel术语叫‘数组’)的一些特征。如函数AVERAGE(array)返回数组的平均值,STDEV(array)返回数组的标准差,MAX(array)和MIN(array)返回数组的最大值和最小值。
为了考察数据集的分布,还要了解一些其他的函数。例如,QUARTILE函数返回一个数据集的四分位数,而FREQUENCY函数则返回一个数据集分组后的频率分布。
Excel还提供一些概率分布函数,如正态分布函数NORMSDIST和正态分布反函数NORMSINV等。
还有一些有用的二元(二个变量)统计函数,它们在进行回归分析和相关性分析时用处很大。例如:
INTERCEPT(known_y’s,known_x’s)
SLOPE(known_y’s,known_x’s)
RSQ(known_y’s,known_x’s)
STEYX(known_y’s,known_x’s)
CORREL(known_y’s,known_x’s)
COVAR(known_y’s,known_x’s)
还有一个大家不太熟悉的数组函数LINEST(known_y’s,known_x’s),它以数组的形式返回一些必要的回归统计量。此类函数将在节介绍回归分析内容时作详细说明。我们还将把它们的返回值与数据回归分析过程中的回归结果作对比。
下一节,我们将通过AMFEXCEL工作簿里Frequency and Snorm表中的例子来说明如何使用FREQUENCY、QUARTILE以及各种正态分布函数。
使用频率函数Frequency
FREQUENCY(data_array,bins_array)统计一个数据集中出现在特定间隔(或‘bins’)中的元素个数,并以一竖列数组返回。‘bins_array’为用于对 ‘data_array’ 中的数值进行分组的间隔数组。由于该函数是以数组的形式返回,所以在输入函数之前,必须在电子表格中为返回值选定一个相邻的单元格区域。
我们从AMFEXCEL工作簿Frequency and Snorm表中的一个例子出发,来说明如何使用FREQUENCY函数。如图所示,D10:D71列和E10:E71列中的月收益和对数收益(采用LN函数)数据的统计信息放在第4到第7行。假设现在我们想得到对数收益(E10:E71),即所谓‘data_array’的频率分布。目的是为了检测这些数据是否近似地服从正态分布。首先,我们为分组确定间隔。观察一下最大和最小的对数收益,范围在到+之间,分为10到12个间隔比较合适。将间隔值输入到G5:G14中,这些值作为对数收益分组的上边界。
Return for months 1-62:1-62月份的收益数据;
Summary Statistics:统计量;
Returns:收益;
Ln Returns:对数收益;
Frequency Distribution:频率分布;
Mean:均值;St Dev:标准差;Max:最大值;Min:最小值;interval:间隔;freq:频数;
%freq:频率百分比;%cum freq:累积频率百分比;Month:月份;Total:共计
【参照书中第13页的图】
图 计算对数收益数据的频率分布
为了正确地输入FREQUENCY函数,先选定单元格区域H5:H15。然后键入=,并点击粘贴函数按钮(标记为 fx)来完成句法:
=FREQUENCY(E10:E71, G5:G14)
在输入完最后一个括弧‘)’后,将鼠标放在Excel的编辑栏,然后按下Ctrl+Shift+Enter组合键,就可以完成函数输入了。(你需要用三个手指同时按下,否则无效。如果失败了,使输出范围继续保持在‘选中’状态,然后按下编辑键(F2),必要时重新编辑公式,然后再次按下Ctrl+Shift+Enter键。)
此时你会看到单元格中的函数被一对大括弧‘{}’括起来了,而且单元格G5:G15中出现了频率数组。结果如图所示。在单元格H17中使用SUM函数检查一下频率数据之和是否等于62。
查看结果,我们可以看出,没有低于的对数收益,处在和之间的有6个值,而大于的对数收益也没有。(FREQUENCY返回数组的最底下单元格G15中,包含了超过‘bins’上限的数值个数。)
由于FREQUENCY函数的输出是数组形式,所以不能对其中的某个单元格作修改。如果间隔数组改变了,则当前的输出数组必须先删除,然后重新输入函数。
可以将计算出来的频率转换为百分比频率(分别除以数据集的大小62),并进而计算出累积百分比频率,如图中的I列和J列所示。百分比频率和累积百分比频率公式可以在Frequency表中看到。
Return for months 1-62:1-62月份的收益数据;
Summary Statistics:统计量;
Returns:收益;
Ln Returns:对数收益;
Frequency Distribution:频率分布;
Mean:均值;St Dev:标准差;Max:最大值;Min:最小值;interval:间隔;freq:频数;
%freq:频率百分比;%cum freq:累积频率百分比;Month:月份;Total:共计
【参照书中第14页的图】
图 对数收益的百分比频率和累积百分比频率分布
显示累积百分比频率的最好方法是用XY图,数据点用一条没有标记的平滑曲线连接。要生成图中所示的图形,可以选择单元格区域G5:G14和J5:J14作为数据源。注意,如果要选中一些不连续的区域,可以先选择第一个区域,然后按下Ctrl键不放松,再选择第二个以及后继的区域。
Frequency Distribution:频率分布;
interval:间隔;freq:频数;theory:理论值;Cumulative Frequency:累积频率;
actual:实际值;
%freq:频率百分比;%cum freq:累积频率百分比;Month:月份;Total:共计
【参照书中第14页的图】
图 累积百分比频率图(实际数据和严格正态分布的数据)
对于正态分布的对数收益,累积分布呈一个S形(如图中的虚线所示)。而实际的对数收益数据则可能是由于偏度的原因,与正态分布有一些偏离。
使用分位数函数Quartile
QUARTILE(array,quart)函数返回数据集四分位数。其中的第二个参数‘quart’是一个整数,表明返回哪一个四分位数:即,quart=0时,返回数组的最小值;quart=1时,返回第1/4分位数(即排序后,处于数组25%处的值);quart=2时,返回中位数(50%);quart=时,返回第3/4分位数(75%);如果是4,返回最大值。
QUARTILE函数提供了一个快捷并相对容易的方法来得到一个数据集的累积分布。例如在图的单元格H22中输入:
QUARTILE(E10:E71,G22)
这里G22中的值为1,于是该函数返回第1/4四分位数。单元格中显示的数值为,也就是说,在选定的数组中,低于的对数收益占整个数据集的25%。第2/4分位数为,是中位数,第3/4分位数为,低于此数的对数收益占整个数据集的75%。图还画出了区域H21:H25的XY图,并对数据点作了标记。可以看出,基于五个数据点上的累积分布曲线与图中的分布曲线颇为相似。
Quartiles:四分位数值;Cumulative Frequency from quartiles:四分位点的累积频率
【参照书中第15页的图】
图 使用QUARTILE计算Frequency表中的对数收益累积分布
第节演示如何在VBA中进行数组处理时会用到QUARTILE函数。与QUARTILE相关的函数,PERCENTILE(array,k),返回一个数据集的第k个百分位点的数值,第节演示如何编写数组函数时会用到此函数。
使用正态函数Norm
在统计类函数中,与正态分布有关的函数名称都是以NORM开头的,后面跟着字母S时,则专指标准正态分布。
NORMSDIST(z)返回标准正态累积分布函数值。NORMSINV(probability)返回给定概率下标准正态分布的分位数。
函数NORMDIST(x,mean,standard_dev,cumulative)的功能则强大的多,它适用于任何正态分布。如果‘cumulative’的值为1(或TRUE),则返回累积分布函数值;如果‘cumulative’的值为0(或FALSE),则返回概率密度函数值。图显示,在Norm表中,单元格C5和D5中分别输入的是概率密度和左尾概率。这两个单元格公式都使用了NORMDIST函数,且均值和方差均为0和1。在C5中,函数最后一个输入项(‘cumulative’)的值为0,因此返回值为概率密度,而在D5中,其值为1,从而得到左尾概率。给定左尾概率时的分位数可以用NORMINV函数得到,如单元格F5所示。
拷贝这些公式,并检测其结果,进一步熟悉这些函数。
最后,我们将得到对数收益的累积百分比频率分布。检验正态性的方法之一是采用观测数据的均值和方差,利用NORMDIST函数计算一组理论上的百分比频率。结果见工作表Frequency中的K列(见图)。理论分布与实际收益分布在图中一起显示。很明显,两者之间有一定的差距。
Excel Normal Functions for N(0,1):Excel正态分布函数
【参照书中第17页的图】
图 工作表Snorm中的一般正态分布函数
Excel提供了大量的函数来进行数据汇总和理论分布建模。本书的股票和期权部分将用到它们。
查找类函数
在含有相关信息的表格中,查找函数可以根据不同的输入信息,检索出其相关的信息。例如,在图中,我们利用VLOOKUP函数从波动率及对应的期权价格表中检索出给定波动率下的布莱克-舒尔斯期权价格。(布莱克-舒尔斯公式的基本理论见第11章。)
一般来讲,函数VLOOKUP(lookup_value,table_array,col_index_num,range_lookup)可以在给定表格(‘table_array’)中的最左一列中查找出匹配值,然后返回同一行中指定列(‘col_index_num’)中的数值。缺省情况下,表格中的第一列是按升序排列的(这就暗示range_lookup=1(或TRUE))。实际上,在本例中,最后一个参数可以忽略不写。
关于查找的例子可以在工作表LookUp中找到。为了检验你是否已经理解,可以用VLOOKUP函数查找出不同销售额下应付的佣金,佣金率放在单元格区域F5:G7中。然后滚动到下面的布莱克-舒尔斯期权价格查询表,如图所示。
所要查找的波动率放在C17(20%)中,表格数组为F17:G27,波动率按升序排列,期权价格在表格的第二列。因此,单元格D18中的公式为:
VLOOKUP(C17, F17:G27,2)
返回波动率为20%时对应的期权价格为。
Black-Scholes Call Value Lookup Table:布莱克-舒尔斯看涨期权定价表;
Volatility:波动率;BS Call Value:布莱克-舒尔斯看涨期权价值;
【参照书中第17页的图】
图 工作表LookUp中查找给定波动率下的期权价格
要查找的值(‘lookup_value’)可以近似(或精确)地匹配表格中第一列的数据,在匹配的基础上找到同一行中指定列的数据,并返回。可以试着在C17中输入不同的值,如%、%等,看看这个查找函数是如何工作的。
‘range_lookup’参数是一个逻辑值(TRUE或者FALSE),表明是进行近似匹配还是精确匹配。如果是TRUE或忽略,则进行近似匹配。这时如果没有找到完全相同的值,就会用比‘lookup_value’小的最大值来匹配。如果是FALSE,VLOOKUP函数将进行精确匹配,这时没有找到完全相同的值就会返回错误值#NA。
与VLOOKUP类似的函数HLOOKUP则对行数据进行匹配,先查找表格中第一行中的匹配数据,然后返回同一列中指定行的数据。
MATCH和INDEX也是查找类函数,参看图中的演示。函数MATCH(lookup_value,lookup_array,match_type)返回单一列(或行)中在指定方式下(‘match_type’),与指定数值匹配的数组元素的相对位置。注意该函数返回的是匹配数值在数组中的位置,而不是数值本身。
如果match_type为0,函数则返回精确匹配的数值位置,不管数组如何排序。如果match_type为1,则返回近似匹配的数值位置,此时需要数组按升序排列。如果match_type为-1,也返回近似匹配的数值位置,但此时需要数组按降序排列。
在图中,G列中的期权价格按升序排列。为了找出数组中匹配的数值位置,可以在D22中输入公式:
=MATCH(C21,G17:G27,1)
其返回值为6。
函数INDEX返回表或区域里特定行列中的数值。在图中,特定的行和列为单元格C25和C26中的值,于是返回数组F17:G27中第六列、第二行中的值。
Black-Scholes Call Value Lookup Table:布莱克-舒尔斯看涨期权定价表;
Volatility:波动率;BS Call Value:布莱克-舒尔斯看涨期权价值;
【参照书中第18页的图】
图 工作表LookUp中的函数公式和结果
如果数组是单一列(或者单一行),参数‘col_num’(或‘row_num’)可以为空。你可以改变一下单元格D27中的值来练习函数INDEX在这种情况下的使用方法。
我们将在本书的股票部分应用VLOOLUP、MATCH以及INDEX函数。
其他类型函数
在介绍电子表格公式的同时,我们也想尽可能地介绍一些‘通用’的公式,它们可以处理一些相关但不完全相同的情况。例如,图中显示的是某只债券在某年的现金流,可以是0,可以是利息,也可以是本金加上利息。
Bond Cashflows:债券现金流;Cash Flows for bonds:债券的现金流量
【参照书中第18页的图】
图 工作表Bonds中含有混合寻址以及嵌套IF函数的通用公式
IF函数给出了两种状况下的不同结果,而含有一级嵌套的IF函数则可以给出三种不同的结果(嵌套越多,可给出的结果也越多)。单元格C11中的现金流公式含有一级嵌套:
=IF($B11<C$6,C$5,IF($B11=C$6,100+C$5,0))
如果把C11中的公式拷贝到C11:H13中,就会得到每只债券每年的现金流。
对于类型1的债券,现金流依赖于不同年份(单元格B11)和债券到期时间(C6)。如果年份在到期时间之前(B11<C6),则现金流就是利息C5;如果刚好到期(B11=C6),则现金流为本金加上利息;其他情况(B11>C6)则现金流为0。内嵌的IF函数处理到期年份以及到期年份之后的现金流,而外围的IF函数则处理第一种状态下的利息收入。
在输入公式时我们采用了‘混合寻址’方式,这是为了保证在拷贝单元格公式时,引用能够作正确地变化。我们输入C$6和C$5是为了保证在沿着列C向下拷贝时,始终将行5和行6中的数据作为到期年份和利息率。而$B11则随着年份的不同变为$B12和$B13。我们输入$B11,则当公式拷贝到D列中时,B列中的数据仍然作为年份来计算,而C$5和C$6则变为D$5和D$6。
在复制复杂模型的结果时,编写这种通用公式得到的收获比节约的时间更有价值。
审核工具
对于复杂的单元格公式,使用审核按钮组将会给你很大的帮助。从菜单栏中调出该按钮组的方法之一,是点击视图菜单下的工具栏,然后选择用户定义…。在如图中所示的用户定义对话框中,选中审核,就会出现审核按钮组。
【参照书中第19页的图】
图 访问审核工具栏及其主要按钮
这组按钮如图所示,它们的名称从左到右分别为追踪引用单元格、移去引用单元格追踪箭头、追踪从属单元格。
回到电子表格,选中单元格C11,然后点击追踪引用单元格按钮,就可以显示出单元格C11中引用到的单元格,如图所示。(图中也显示了F13中引用到的单元格。)点击移去引用单元格追踪箭头就可以清除这些箭头。
Bond Cashflows:债券现金流;Cash Flows for bonds:债券的现金流量
【参照书中第20页的图】
图 工作表Bonds中的追踪引用单元格按钮使用演示
用户定义对话框可以按你的意愿来定制工具栏。如果你点击命令页,并选中某一类别,你可以选择并拖动命令列表中的工具按钮将按钮拖到工具栏中。相反,你也可以选择和拖动按钮将按钮移出工具栏(使它们返回工具框)。
模拟运算表(Data Tables)
模拟运算表(Data Tables)可以执行单元格公式一系列的重复计算,而不用拷贝或重新输入公式。AMFEXCEL工作簿中有不少模拟运算表的应用实例。这里用CompoundDTab工作表中计算复利和折现因子的例子说明单变量和双变量输入模拟运算表的使用方法。在工作表DSBTab中也有其他一些关于模拟运算表应用的例子。
建立单变量模拟运算表
图给出了利率为5%的年连续复利因子(单元格C10中)。利率为5%的年折现因子则放在单元格C11中。单元格中的公式也同时显示出来。
可以得到包含不同时段复利因子和折现因子的表格,如t=1、2、一直到10年。用模拟运算表来完成这项任务,首先规划一下适当的版面,如图中的第16行及其下方所示的那样。
需要重复计算的公式放在模拟运算表的第一行(行16)。因此在D16中,公式为‘=C10’,这使得它与单元格C10中的公式联系起来。同样的,在E16中,公式为‘=C11’。作为输入变量,时间t的信息则放在C列,其起始行在公式行之下。注意,单元格C16是处在公式行中,并且它的值为空。图例子中所谓的表格区域就是C16:E26。
Continuous Compounding:连续复利;
Enter formula(s) for output:输入公式;
Enter numbers for input variable t:为参数t输入数字;
【参照书中第21页的图】
图 工作表CompoundDTab中单变量输入模拟运算表版面布局
现在工作表就可以进行模拟运算表计算了,只需简单地做如下操作:
选中表格区域C16:E26
在主菜单中,点击数据中的模拟运算表
在对话框中:列输入单元格中输入单元格C7,然后点击确定按钮
结果如图所示,表中单元格格式的可读性更强。单元格中显示的是数字,但实际上包含的是数组公式。这些数值是动态的,如果假定的数据如利率r,或变量t发生了改变,则它们将被重新计算一遍。可以试着将C5中的利率改成6%,然后观察单元格数值的变化。接下来,请将利率改回5%。
Compounding/Discounting Factors:复利/折现因子;
Enter formula(s) for output:输入公式;
Enter numbers for input variable t:为参数t输入数字;
【参照书中第22页的图】
图 模拟运算表中不同时段的复利因子和折现因子
建立双变量模拟运算表
假设我们希望计算出不同利率不同时段的折现因子(单元格C11中的公式)。同样,首先必须规划双变量输入的模拟运算表版面布局。一种可能的版面布局如图所示。
Enter formula(s) for output:输入公式;
Enter numbers for input variable t:为参数t输入数字;
【参照书中第22页的图】
图 CompoundDTab中双变量的模拟运算表版面布局
这里,表格区域为C30:I40。第一列数值是计算折现因子需要用到的时间t(列输入变量)。第30行中则列出五种利率值(行输入变量)。在表格左上角的单元格(C30)中则包含着根据不同利率和时间计算折现因子的公式。C30中的公式为‘=C11’,它与C11中的折现因子计算公式相联系。
计算表格中数据的步骤如下:
选择表格区域C30:I40
在主菜单中,点击数据中的模拟运算表
在对话框中:列输入单元格输入C7
行输入单元格为C5
然后点击确定按钮
其结果如图所示。将图中利率为5%时计算出的数据与图中的数据做一个对比,看看是否一致。
Enter formula(s) for output:输入公式;
Enter numbers for input variable t:为参数t输入数字;
【参照书中第23页的图】
图 模拟运算表中不同利率不同时段的折现因子
模拟运算表在处理‘what-ifs’类问题时极为有用。其优点在于表格能够自动地适应模型的变化。而其缺点在于:如果工作表中含有大量的模拟运算表,当输入改变后,连续的重新计算会降低处理速度。基于这个原因,有时可能需要关掉模拟运算表的自动重算功能。
建立模拟运算表需要注意一下几点:
到目前为止,Excel要求模拟运算表的输入单元格必须与模拟运算表在同一个工作表中。
模拟运算表单元格中的公式是以数组形式出现的,例如,模拟运算表单元格的输入为{=TABLE(C5,C7)},其中的C5和C7是输入单元格。既然是以数组的形式出现,那么你就不能编辑它。
如果要重建或者扩建模拟运算表,可以先选中所有包含了{=TABLE()}的单元格,然后点击编辑菜单下的清除所有,或者直接按Del键。
改变输入或其他假定的数据,会导致模拟运算表重新计算,除非缺省的计算方法做了有意的改动。
在大型模型中,模拟运算表中每一个输入的重新计算可能需要很长的时间,这时可以关闭模拟运算表的自动重算功能。要做到这一点,可以进行如下操作
点击工具菜单下的选项
点击重新计算页,选中除模拟运算表外,自动重算选项
当自动重算功能关闭后,按下F9,强迫所有的模拟运算表重新计算一遍。
如果你对期权定价的布莱克-舒尔斯公式很熟悉,你可以通过建立工作表BSDTab中建议的三个模拟运算表来巩固模拟运算表方面的知识。它同样可以用来计算布莱克-舒尔斯看涨期权价格对即期股票价格S的敏感度,以及对其他输入变量的敏感度。
XY图
Excel提供了许多类型的图表,但是在数学、科学和金融方面,XY散点图是最有用的。在不会产生混淆的情况下,我们简称其为XY图。关于XY图,很重要的一点是它的X轴和Y轴都是数值刻度。而所有其他的双轴图表(包括折线图),只有纵坐标轴是数值刻度,而横坐标轴则是标签型。
可以用图表向导来建立一个XY图,共有四个步骤,它们分别是图表类型、图表源数据、图表选项和图表位置。其中最重要的是第二步,图表源数据。这些步骤已经在节中讨论单输入变量模拟运算表的结果时提到,并在图中作了演示。CompoundDTab工作表中画图所用到的模拟运算表结果区域为C17:E26,C列中的数据作为x值,D列和E列中的数据用来作图。选中作图的区域后,按下列步骤进行:
点击主工具栏中的图表向导按钮。(它像一个小的条形图。)在步骤1的对话框中(如图所示),选择图表类型;这里,选择XY散点图子图表类型中的无数据点平滑线散点图。点击按下不放可查看示例按钮。如果觉得不错,就可以点击下一步按钮。
【参照书中第24页的图】
图 图表类型对话框
在步骤2的对话框中,检查数据区域输入框中的数据源是否是正确的,注意,Excel将这个数据区域理解为三列数据。点击系列页,可以看到系列1中的X值和Y值。点击名称输入框,选择工作表中单元格或是直接键入(compound)的方式为当前激活的系列加一个名称。图中显示系列2的名称改成了单元格E15中的discount。点击下一步按钮继续。
【参照书中第25页的图】
图在图表源数据对话框中指定X值和Y值
在步骤3的对话框中,输入图表选项,如标题、网格线、图例等。在我们的例子中,象图中显示的那样,增加标题并去掉网格线就够了。点击下一步按钮继续。
【参照书中第25页的图】
图 图表选项对话框
在步骤4的对话框中,决定图表位置。图表可以作为工作表的一部分(嵌入式图表),也可以作为一个分开的图表页。通常是作为一个嵌入式图表,这样就可以看到图表源数据变化的效果了,如图显示的那样。
【参照书中第26页的图】
图 图表位置对话框
从专业的角度来看,图这种未经加工的图表还需要做进一步的修饰,用以改进它的外观。主要的改进有,将绘图区的背景颜色改为白色(无背景颜色),在图表选项中的坐标轴页里改变坐标轴,就像在标题和图例中那样。经常需要改变的是坐标轴刻度,以及标题的字体等。绘图区的大小也需要调整。有的数据系列可能需要显示数据点(激活数据系列并重新设定格式)。通过修改,就可以得到图中显示的那种图表了。
Compounding/Discounting Factors:复利/折现因子;
【参照书中第26页的图】
图 由图表向导生成的未经加工的图表
访问数据分析和规划求解
Excel有一些附加的模块,这些模块在完全安装时是可用的,但如果选择节省空间的安装,则被忽略。我们需要用到规划求解和数据分析回归过程,因此需要先检查一下工具菜单,看看它们是否可用。如图,其中含有规划求解和数据分析选项。如果工具菜单中没有,你可以点击加载宏选项(也在工具菜单中)。在如图所示的对话框中选中分析工具库、分析工具库-VBA函数和规划求解,然后点击确定按钮。安装完毕后,你就可以在工具菜单中看到这两个选项了。
【参照书中第27页的图】
图 含有数据分析和规划求解选项的工具菜单,以及加载宏对话框
在解最优化问题时,可以对规划求解的使用作最好的描述,因此这部分内容将放在本书股票部分的节,在那里,使用规划求解来求解最优的组合权重。
在利用函数和分析工具库来进行回归分析之前,我们先简单地介绍一下区域名称,它在选择和引用大范围数据时是很有用的。
使用区域名称
图显示的是AMFEXCEL工作簿Beta表中的股票A和股票指数收益的开始部分。在接下来的一节里,我们要对股票收益和指数收益进行回归分析,看看它们之间是否存在一定的关系。为了详细说明计算过程,先给包含数据的单元格区域取一个名称是很有用的,比如说,将数据区域B5:B64命名为ShareA,将数据区域C5:C64命名为Index。
EMBED PBrush
【参照书中第28页的图】
图 带有名称的数据区域,粘贴名称按钮以及定义名称对话框
需要命名的区域被选中后,就可以在主菜单栏左下方的名称框里键入你想取的名称,如图所示。这样,在Beta工作表中,名称ShareA就与区域B5:B64绑定起来。另外,还可以选择插入菜单下的名称选项,然后点击定义选项来为选中的区域命名,在出现的对话框中键入名称ShareA即可,如图所示。这样,选择和引用收益区域时就可以用它的名称来代替了,比如选定名称框中的名称ShareA,或是在输入函数时使用粘贴名称按钮。
回归分析
假设读者已经对简单的回归分析(两个变量)很熟悉,在本书的股票部分将用到它。这里,我们概述一下怎样利用Excel来进行必要的计算。实际上,Excel有多种方法来进行回归运算,主要有两类:使用Excel函数和使用分析工具库中的简单回归程序。我们将用Beta工作表中的股票和指数收益来演示这两种不同的简单回归方法,首先用Excel函数,然后使用数据分析回归过程。如果你对这些函数和过程不熟悉,Beta工作表提供了一个适当的练习版面。
Excel提供了一些进行回归分析时经常用到的函数。图展示了回归方程的截距、斜率函数,以及两种拟和度函数:R平方和标准差(STEYX表示‘standard eror of Y given X’)。由于数据区域已被命名,所以可以用粘贴名称按钮把它们的名称输入到函数中。这些函数结果会随着输入数据(ShareA和Index区域中的数据)的变化而动态变化。
Excel regression functions:Excel回归函数;
【参照书中第29页的图】
图 Beta工作表中的Excel回归函数
除了上面提到的这些函数,还有一个LINEST数组函数,它以两列收益数据为输入项,返回数组形式的基本回归量。注意,在书写LINEST句法时,为函数的输出选择一个适当大小的区域是很重要的。图显示了函数LINEST(ShareA,Index,,1)在函数参数面板中输入参数时的情况,此时输出区域F40:G44已经被选定。‘Const’参数为空,而‘Stats’参数为1,这时会输出全部的回归统计值。
【参照书中第29页的图】
图 在公式面板中建立LINEST数组函数
数组输出如图所示(包括注解)。除了斜率和截距(这里它们的标记分别为Beta和Alpha),LINEST还提供了拟合的标准差,以及一个基本的方差分析结果(也叫‘ANOVA’)
Output from Linest(Remember Ctrl+Shift+Enter):
Linest函数的输出结果(记住要按下组合键Ctrl+Shift+Enter)
【参照书中第30页的图】
图 Beta表中采用LINEST函数来分析收益数据的数组输出
下一步,我们来练习一下分析工具库中的回归过程。首先,点击菜单工具下的数据分析选项,然后点击回归,于是就出现了图中显示的对话框。同样,对话框中的Y区域和X区域可以通过选择单元格区域来引用,也可以通过输入名称来引用(如果区域已经命名的话)。一般来说,指定一个数据表中左上角的单元格作为输出区域比接受缺省输出选项(将结果输出到一个新工作表中)更方面一些。
【参照书中第30页的图】
图 设定分析工具库的回归计算以及输出选项
结果如图所示,回归方程的截距和斜率放在单元格F23和F24中。R平方和标准差(残差的标准方差)放在F12和F13中。输出是静态的,也就是说,这些数值不再随着输入数据的变化而变化。如果任何输入数据发生了改变,分析过程是不需要调整的,但必须手动重新计算。
【参照书中第31页的图】
图 Beta工作表中分析工具库回归程序的分析结果
为了对比演示函数的动态特性,我们假设4月份的指数后来被更正为(不是现在C8中的)。如果输入改变了,则F31:F34以及F40:F44中的函数结果会立即更新,例如,RSQ会从变为。而要想更新分析工具库的分析结果,则必须将回归过程重新运行一遍。
分析工具库程序的静态输出特性使得它们在计算时不像Excel函数那样有吸引力。这些程序是由Excel版本4中的旧XLM宏语言编写的。更重要的是,不同于大多数Excel函数,它们很难在VBA用户定义函数中被调用。
单变量求解
单变量求解是另一类Excel静态过程。这种工具可以在已知单个公式预期结果的情况下,求解公式的输入值。例如,在图中,股票期权的布莱克-舒尔斯看涨期权价格与市场价格不一致。(布莱克-舒尔斯期权价格依赖于标的股票的波动率,并需要对未来波动率作估计。)假设我们想要知道波动率大小为多少时,能使得两者一致,也就是使得两者之间的差距(G9)为0。这是单变量求解的一个典型应用。
在BSDTab表中使用单变量求解,可以点击工具菜单下的单变量求解选项。填写如图中所示的输入框,然后点击确定按钮。单变量求解的处理结果如图所示,要使布莱克-舒尔斯期权价格与市场价格一致,波动率必须为23%。
Black-Scholes Option Values:布莱克-舒尔斯期权价值;
Black-Scholes Call Value:布莱克-舒尔斯看涨期权价值
【参照书中第32页的图】
图 BSDTab表中的BS看涨期权价格和市场价格
【参照书中第32页的图】
图 利用单变量求解求波动率(C9),使得BS价格与市场价格一致
Black-Scholes Option Values:布莱克-舒尔斯期权价值;
Black-Scholes Call Value:布莱克-舒尔斯看涨期权价值
【参照书中第32页的图】
图 单变量求解求出的波动率大小
单变量求解从一个初值开始,然后采用一种迭代方法使结果与设定值逐渐靠近。可变单元格中的数值就作为初值,在这里是20%。求解时,你可以改变一个单元格中的输入值,也可以改变多个单元格中的输入值(见第6章)。
作为练习,可以将市场价格改为9,然后在BSDTab表中用单变量求解来求出相应的波动率。
矩阵代数以及相关函数
矩阵的概念在代数中应用非常广泛,它为结构相似的方程提供了一个紧凑的表达形式。矩阵运算看起来很像代数运算,但是矩阵相乘却显得很复杂。Excel包含了一些有用的矩阵函数,它们属于数学类函数,要想熟练地使用它们,必须先了解一些矩阵的基本背景知识。接下来的一节,我们将解释一下矩阵的概念,然后介绍矩阵的转置、相加、相乘以及逆运算。演示这些运算的例程放在AMFEXCEL工作簿的MatDef表中。对矩阵很熟悉的读者,可以跳过这节内容,直接看一下矩阵函数小结()就可以了。
矩阵介绍
在代数中,一个矩形的数值数组就是矩阵。只有一列数值的矩阵通常被称为列向量;同样,只有一行数值的矩阵被称为行向量。在Excel中,矩形的单元格区域被称为数组。下面的这些数值区域都可以看成为矩阵:
在这里‘||’只是一个符号。如果我们将上面的四个矩阵分别起名为x、y、A和B,则x就是一个列向量,y是一个行向量。矩阵A有三行三列,因此是一个方阵。B不是方阵,因为它有四行和三列,它是一个4×3的矩阵。行数r和列数c给出了一个矩阵的维数,有时写成(r×c)的形式。例如,如果:
并且
则x的维数为(2×1),而y的维数为(1×2)。
矩阵转置
矩阵的转置是将矩阵的行转换为列(反之亦然)。很明显,列向量x的转置是一个行向量,表示为。图中的电子表格演示了列向量x和行向量y的转置过程。
TRANSPOSE函数作用于数组区域,并返回它的转置矩阵。例如,单元格C4:C5中2×1向量x的转置矩阵维数为(1×2)。使用TRANSPOSE函数时,先选中单元格区域I4:J4,然后键入公式:
=TRANSPOSE(C4:C5)
然后按下组合键Ctrl+Shift+Enter。其结果如图所示。
Array Manipulation:数组操作;Transposes:转置;
Array:数组;dim:维数;
【参照书中第34页的图】
图 MatDef工作表中演示的矩阵运算
矩阵相加
两个矩阵相加是将对应的元素分别相加。因此,这两个数组必须具有相同的维数。所以,x和y是不能相加的,而x和具有相同的维数,2×1,它们是可以相加的,其结果如下:
如果将向量y数乘以10,则y中的每一个元素都乘以10。结果如下:
这相当于10个y相加。
矩阵相乘
两矩阵作相乘运算时,必须有一个维数是相等的,即,一个矩阵的列数必须等于另一个矩阵的行数。简称其为‘维数一致’。如果要得到乘积xy,则x的列数必须等于y的行数,(2×1)乘以(1×2),其结果为一个(2×2)的矩阵。
在图中,单元格C10:D11中乘积xy的计算过程如下:
就是说,乘积xy第一行第一列的元素是x第一行元素乘以y第一列元素的结果,如此类推。
作为对比,乘积yx是维数(1×2)乘以(2×1),结果为(1×1),也就是说,它只包含一个元素。观察单元格C13中的乘积yx,其计算过程如下:
这些计算结果表明,乘积xy与乘积yx是不相等的。所以相乘的顺序很重要的。
MMULT数组函数返回的是两个矩阵‘array1’和‘array2’的乘积。因此,为了得到乘积xy的(2×2)矩阵元素,必须先选中一个2×2的单元格区域,然后键入或利用粘贴函数按钮输入如下的公式:
=MMULT(C4:C5,C7:D7)
最后记住同时按下组合键Ctrl+Shift+Enter。
如果数据区域C4:C5和C7:D7分别命名为x和y,则公式可以简单地写成:
=MMULT(x,y)
再考虑两个大点的数组:
并且
C和D的维数分别为(2×2)和(2×3),既然 C的列数与D的行数相同,因此乘积CD是可以得到的,它的维数是(2×3),计算过程如下:
但是,乘积DC却不能成立,因为两者的维数不匹配(D的列数不等于C的行数)。一般地讲,矩阵相乘是不能互换的,所以通常CD≠DC。
如果C和D分别是上面两个数组的名称,则单元格公式:
=MMULT(C,D)
将生成一个(2×3)的数组。
矩阵求逆
一个方阵I,它的对角线上的元素全为1,而其它的元素全为0,我们叫这个方阵为单位矩阵。因此:
是一个单位矩阵。
假设D是前面提到的(2×3)的矩阵,而I是一个(2×2)的单位矩阵。那么:
任何矩阵与一个适当维数的单位矩阵相乘都等于该矩阵本身(就如同与1相乘一样)。
现在假设A是一个维数为n的方阵,也就是一个n×n矩阵。如果存在一个方阵,满足下面的条件,我们就把它叫做矩阵A的逆矩阵:
例如,
则
并且
求一个矩阵的逆矩阵是一件很麻烦的事。幸运的是,函数MINVERSE可以直接求矩阵的逆。例如,为了得到图中矩阵A的逆矩阵,我们先选中一个3×3的单元格区域I17:K19,然后输入数组公式:
=MINVERSE(C17:E19)
就可以了。读者可以将结果与A相乘来检测一下它是否正确。
Array:数组;dim:维数;
【参照书中第336页的图】
图 MatDef工作表中显示的矩阵逆运算
线性方程组求解
矩阵逆运算的用途之一是对如下形式的方程组求解:
它们可以写成矩阵的形式:Ax=b,其中:
要想解这组方程,可以将方程的两边分别乘以矩阵A的逆矩阵:
,因此 也就是
在图中,解向量x就是在单元格区域中利用矩阵相乘求得的:
=MMULT(I17:K19,C21:C23)
并不是所有的线性方程组都有唯一解,在一些特殊条件下,它可能有多个解。方程组Ax=b有唯一解的充要条件是:矩阵A为方阵,且它有逆矩阵。通常,它的解由求得。
Excel矩阵函数小结
汇总一下,Excel中有进行矩阵转置、矩阵相乘、矩阵求逆运算的函数。对应的函数如下:
TRANSPOSE(array) 返回一个矩阵的转置
MMULT(array1, array2) 返回两个矩阵的乘积
MINVERSE(array) 返回一个矩阵的逆矩阵
由于这些函数是以数组形式输出的,因此输出数组的大小必须预先知道。在选中适当大小的区域之后,才能键入公式(或者利用粘贴函数按钮以及函数参数面板)。最后按下组合键Ctrl+Shift+Enter而不是简单的Enter键来输入公式。如果操作失败,可以保持输出区域在‘选中’状态,然后按下编辑键(F2),必要时重新输入公式,最后再按一次Ctrl+Shift+Enter组合键。
为了巩固所学的知识,可以试着做一下MatExs工作表中的练习。
我们将在本书的股票部分大量地使用到矩阵函数,不管是用于电子表格计算,还是用于VBA用户定义函数。
小结
Excel中有各种类型的函数和过程。它们包括数学、统计和查找类函数,以及常用的过程,如建立模拟运算表和用XY散点图显示结果等。
要访问函数,可以点击粘贴函数按钮,函数的参数可以在函数参数对话框中输入。区域名称的使用可以简化操作,特别是当区域比较大的时候。区域名称可以用于函数参数对话框中。
审计工具条给我们带来很大的方便,特别是追踪引用单元格、移去引用单元格追踪箭头和追踪从属单元格按钮,它们在检查公式时非常有用。
熟悉各类函数是很必要的,因为它们能够非常容易地嵌入到用户定义函数中,进行VBA编码时也经常用到它们。
在使用数组函数时需要非常小心。必须预先知道输出数组的区域大小。然后选中适当大小的单元格区域并输入公式,最后按下组合键Ctrl+Shift+Enter。
嵌入式函数是动态的,也就是说,当它们的输入数据发生改变时,函数结果也会随之变化。相比之下,一些过程如单变量求解和分析工具库中的程序则是静态的。数据堆栈(data dump)的结果与初始数据是没有关联的。因此当输入数据改变时,这些过程必须重新运行一遍。
第3章 VBA介绍
本章通过实例来介绍Excel中VBA和宏的应用。VBA可以增强工作表的功能。这里,我们并不打算对VBA编程做全面的介绍,只是想通过例子来掌握一些必要的VBA编程知识,了解一下编程语言的‘面向对象’功能,并采用一种增量方法来控制代码。本章大部分例程都只包含一些简单的编码,第一个例程‘hands-on’出现在节中。然而在节中,有些应用程序被扩展的更加全面,并涉及到宏的应用。其中包括产生图表的子程序,计算并展示正态概率分布图的子程序,以及生成有效边界的子程序。工作簿中包含了本章讨论的所有例程,可以将其作为参考。但还是建议读者新建一个工作簿来编码(不要打开VBSUB工作簿)。
整本书会不断的复习VBA编程知识。后面章节中提到的函数和宏都是以本章和下一章的内容为基础的。
掌握VBA的好处
有经验的用户使用宏是出于两种原因:方便控制重复计算;协助那些对电子表格不熟的第三方用户。从我们的观点来看,掌握VBA的主要目的是能够利用函数和宏来进行自动计算。如果一系列计算由函数代替,则电子表格模型将变得更为强大。Excel提供了各种类型的函数,而VBA可以将函数的范围进一步扩大。另外,使用VBA宏来产生图表或进行自动重复操作非常有用,如进行模拟。在这两种情况下都需要使用VBA,但第一种情况会使用用户定义函数,而第二种情况则使用宏(子程序)。然而两种类型的编程代码大部分都是相同的,本章将集中讲解宏的编写方法,而在第四章则集中讲解用户定义函数的编写方法。
Excel中使用的编程语言叫做Visual Basic for Applications,简称为VBA。单词basic表明这种语言源于古老的BASIC语言,这意味着VBA在处理大量计算时效率不高。当PC机变得越来越强大,并且电子表格的功能越来越多时,电子表格与专业计算软件包之间的界限也变得越来越模糊了。当前的问题是:完成一项任务的最佳途径是什么?如:发现‘有效边界’,模拟一只股票的表现,或者求一个矩阵的‘特征向量’。答案依赖于用户的目的。电子表格的运算速度较慢,但计算过程却很容易理解。而专业软件包的运算速度很快,但其计算过程太过晦涩。
根据要解决的问题来选择VBA的使用方式是很重要的,电子表格并不能提供所有问题的解决方案。使用VBA子程序来处理自动重复计算任务是很有效的;使用VBA建立简单函数也是很方便的;但是想编写强大的交互式程序,使得一个新手能够访问一个复杂的电子表格模型,则似乎非常困难。也许真有程序员可以处理这类问题,但我们并不提倡这样做。VBA不是所有编程任务的万能灵药,它应该被选择性地使用,因为还有一些更为有效的方法来处理那些计算量大的任务。
Excel的宏录制器(macro recorder)可以将用户的按键转化为VBA代码,并且可以用来提供适当的代码。虽然我们可以不必知道底下的VBA代码而直接使用宏录制器,但我们本章的目的是解释并演示一些VBA的实际应用,这样能使你更加有效地使用整个Excel软件包。也许在刚开始时,最好的办法是先用宏录制器产生初始代码,然后在你熟悉VBA代码之后,再选择性地编辑(或重写)这些代码。
了解VBA子程序与函数之间的区别是很重要的。它们之间有较大的差异,包含一段源代码的函数可以返回数值(或数组),而VBA子程序则不能。典型的VBA子程序是没有输入项的,但它们可以执行一系列电子表格命令(这些命令可以使用电子表格单元格中的数值,并改变某些单元格中的数值)。而函数则可以接受输入(或‘参数’),并在执行一系列数值计算后,返回一个数值(或一个数组)。然而,不管是VBA子程序还是函数,电子表格都是第一个发展的工具。
VBA的面向对象观点
首先要掌握一些基本概念,实际上VBA是一种‘面向对象’的编程语言。每一个Excel对象代表Excel中的一个部件或者一项功能,例如:文件、工作表、区域、图表、情节等等,都是Excel的对象,Excel本身也是一个对象(它是应用程序对象)。可以通过VBA编程来操纵Excel对象的属性和调用这些对象。
描述对象和VBA的语言很多,但它们基本上可浓缩为四句相当笼统的话。教科书和VBA帮助文件中对此做了大量的解释,但一般来说,这些解释可能会使你觉得更加混乱。
第一句话:对象是一个集合。例如,Workbooks对象包含了所有已打开的工作簿,Worksheets(或者Sheets)也一样(包含一个工作簿中所有的工作表),Scenarios(一个工作表中所有的情景)、Charts(一个工作表中所有的图表)等等都是如此。然而,有些对象只包含一个成员(也就是只有一个成员的集合),例如,Excel只有一个Application对象(它本身),电子表格中任何一个单元格都只有一个Font对象(尽管这个对象有多个属性,如名称、字体大小等等。)。这些单一的对象可以被直接引用,如简单地写成Application.和Font.(对象后面紧跟一个‘句点’)的形式。集合中的单个对象可以用数字(1,2,3…)或名称索引,如Workbooks(1).或者Sheets(“inputs”).。区域对象是一个单一对象,它的引用方式与集合相似,可以通过名称和地址索引,如Range(“data”).或者Range(“A1:B20”).。
第二句话:对象是有层次的。下面的序列就展示了Excel中对象的层次特点。它显示了工作簿 inputs表中的单元格区域‘data’是如何通过它所处的层次位置被引用的:
(“”).Sheets(“inputs”).Range(“data”)
当单元格区域的名称(这里是‘data’)是唯一的时候,就不需要写出所有的层次。如果工作簿是当前的激活文件,则写成Sheets(“inputs”).Range(“data”)就足够了;或者直接引用激活文件:
(“inputs”).Range(“data”)
同样地,如果只有工作簿被打开,并且工作表inputs是当前的激活页,则写成(“data”)就足够了。
第三句话:对象拥有属性。属性是一个对象的特性,是描述对象的数值或参数。VBA可以给一个对象的属性赋值,也可以读取属性的值。属性的值一般是数字、文本、True或False等等。可以编写VBA代码来改变Excel对象的属性,进而控制它们。例如:
= False
这一行代码关闭了Excel在运行宏时刷新屏幕的功能。ScreenUpdating是Application对象的一个属性,它的值为True/False。
另一个例子:
Range(“B23”).Name=”month2”
Range(“B23”).Value=4000
这个例子将单元格B23的名称设为‘month2’,并将它的值赋为4000。语法采用‘’的形式。在这些例子中,‘Application’和‘Range’是对象,而‘ScreenUpdating’、‘Name’和‘Value’是对象的属性。下面一个例子读取一个单元格的数值,并将它赋给变量‘firstval’:
firstval = Range(“B23”).Value
第四句话:对象拥有方法。方法是一组对象可以执行的,或者可以对对象进行操作的预先定义的行为。下面是调用Range对象方法的例子:
Range(“A1:B3”).Select 选中单元格区域A1:B3
Range(“A1:B10”).Copy 拷贝单元格区域A1:B10中的内容 Range(“storerange”).PasteSpecial 将剪贴板中的内容粘贴到单元格‘storerange’中
语法都采用‘’的形式。在上面的例子中,‘Range’是对象,而‘Select’、‘Copy’和‘PasteSpecial’则是对象的方法。Workbook对象和Worksheet对象也有方法,例如:
Workbooks(“”).Activate 将工作簿激活
Sheets(“inputs”).Delete 删除工作表inputs
上面的解释可能不太完全,还有一些例外。实际录制代码时,你根本不必担心对象属性与方法之间的差别。可以参考Visual Basic编辑器中的Excel对象浏览器,检查特定对象的语法是否正确。
编写VBA宏
掌握任何一种语言都有一个积累的过程,一方面要学习语言规则,另一方面要不断地试验和测试编码。VBA代码是在Excel的编码环境――Visual Basic编码器中编写的。Excel 97的编码功能在Excel 5/7的基础上得到了扩充和增强,为编码提供了更多的支持。我们先从检查一些简单例程的代码入手。VBA的MsgBox函数提供了一种简单的方式来显示计算结果和反馈简单的诊断信息。我们将演示怎样用宏录制器来生成代码,并与手写代码作比较。最后概述一下这两种方法的优缺点。
简单VBA子程序
一个子程序是一段独立的VBA代码;它是程序的基本组成部分。子程序执行一系列动作,它由一些VBA语句组成,并被Sub和End Sub语句包围起来。它的名称后面有一对空括号(除非有另一个子程序给它传递参数)。
例如,下面的LinkOne()子程序将一个单元格中的内容连接到另一个单元格中。代码由一个Sub单词开始,然后是宏的名称,LinkOne。注释语句说明了这个宏的目的。(在文本前加一个单引号,则该语句就会在程序运行时被忽略,这是一个为代码加注释的好方法。)第一条语句使用到Range对象的Value属性,第二句则用到Formula属性:
Sub LinkOne()
‘enters a value in B3,then links cell B4 to B3
Range(“B3”).Value = 4000
Range(“B4”).Formula = “=b3”
End Sub
LinkOne子程序在激活的工作表中起作用。然而,如果这些操作的对象在工作表中存在混淆,就应该更准确地引用单元格。下面的子程序将Inputs工作表中的单元格B3赋值为4000。然后将B3中的内容拷贝到同一个工作表的B4中:
Sub LinkTwo()
‘enters a value in B3,then links cell B4 to B3 on Inputs sheet
Sheets(“Inputs”).Range(“B3”).Value = 4000
Sheets(“Inputs”).Range(“B4”).Formula = “=b3”
End Sub
如果想在当前的激活页中执行此操作,则可以将上面的Sheets(“Inputs”).用ActiveSheet.代替。
接下来的例子演示了Excel录制器根据实际按键生成的代码。将它与子程序Factorial的代码作一下比较。这个子程序计算单元格B5中数值(引用为num)的阶乘(表示为fac),并将结果输出到C5中。它有两个特点。首先,代码使用了变量:i、fac和num。另外,它在一个以i为指针的For…Next循环中采用重复相乘的方式来计算阶乘。(计数器的初始值为1,然后每循环一次,值加1,直到达到它的上限num。)
Sub Factorial()
‘Calculates the factorial of a number in B5
num = Range(“B5”).Value
fac = 1
For i=1 To num
Fac = i * fac
Next i
Range(“C5”).Value = fac
End Sub
除了要读取电子表格中的值和返回值给电子表格外,这段代码还使用了Basic语句,它基本上与Excel无关。这种类型的代码必须手工输入。
交互函数MsgBox
一般来讲,子程序的输入值要么来自电子表格的单元格,要么来自用户输入(通过屏幕),而它的输出值要么输出到文件中,要么直接显示给用户。VBA的内嵌函数提供了两种有用的屏幕显示方法,MsgBox()和InputBox()。相对简单的MsgBox()函数在屏幕上显示一条信息,然后等待用户的反应。代码如下:
Sub MsgBoxDemo1()
MsgBox “Click OK to continue”
End Sub
信息(或‘提示’)必须用双引号引起来。在这个简单的MsgBox实例中,提示两边的括弧是可选的。然而,当需要从MsgBox中得到回应,就必须使用括弧,这时MsgBox就象函数一样使用。如果有一个变量链接到提示上(使用连接符&),则MsgBox会变得更加有用。例如,在Factorial子程序中,可以用下面的语句将结果传递给用户:
MsgBox “Factorial is ”& fac
下面的代码中,用户用InputBox函数来输入数值。这个VBA函数弹出一个对话框,显示一条信息,并返回用户的输入值,这里表示为num。InputBox语句中的参数是需要显示的信息,也就是‘提示’。这里的InputBox有一对括弧,说明它是作为一个函数被调用的,它返回用户的回答,num。阶乘的计算仍是采用一个For…Next 循环:
Sub Factorial()
‘calculates the factorial of a number
fac = 1
num = InputBox(“Enter number”)
For i = 1 To num
fac = i * fac
Next i
MsgBox “Factorial is ”& fac
End Sub
我们将利用这个简单的例子来探讨节中宏编程的组成元素。
编写环境
Visual Basic 编辑器(简称VBE)是子程序编写、测试和调试的地方。假定Excel的工作簿环境、菜单、命令和工具栏都处在常用的位置。当编写VBA程序时,将Excel的Visual Basic工具栏调出来放在Excel窗口中(如图所示),是很有帮助的。就像其他工具栏一样,这个工具栏中的按钮可以执行一系列相关菜单命令,如运行宏、录制宏和调用VBE(从左数第一、第二和第四个按钮)等宏命令工具。
【参照书中第44页的图】
图 Excel中的Visaul Basic工具栏
VBA代码不是写在(或录制在)工作表中,而是写在(或录制在)模块页中。在Excel 97及以后的版本中,它们会出现在一个不同的窗口――Visual Basic窗口中,编写宏时需要在Excel窗口和VB窗口之间切换(使用组合键Alt+Tab或Alt+F11)。
读者可以先作下面的练习:先在Excel中打开一个新文件,选择工具菜单下的宏选项,然后点击Visual Basic编辑器,就可以调出VB窗口。(或直接点击VB工具栏中的VBE按钮。)VB窗口包括工程资源管理器、属性窗口以及代码窗口(这个窗口不常出现,除非工作簿中含有模块)。代码窗口是程序编写和/或按键录制的地方。在VB窗口,可以参考Excel对象浏览器来检查特定对象的VBA语句是否正确。(在VB窗口中,点击视图菜单来访问对象浏览器,然后选择左上角文本框中的Excel。)
VBE中也有一些调试和编辑工具,可以通过命令条或者专门的工具栏来访问它们。有关Visual Basic环境更多的细节可以查看本章后面的附录3A。
输入代码并运行宏
VBA代码在一个模块页中输入。要增加一个模块页到工作簿中,可以点击VB窗口插入菜单中的模块选项,这样就会在代码窗口中出现一个空白的模块页。在代码窗口键入下面的代码:
Sub MsgBoxDemo()
MsgBox "Click OK to continue"
End Sub
在第一句的尾部,按下Enter键后,Excel会自动加上End Sub语句。读者也许已经注意到了VBE的‘快速提示’特性,它可以协助用户输入一些可识别的语句。
可以用多种方法来运行子程序:
在代码窗口,只要将鼠标指示器放在子程序代码体内,然后点击菜单运行中的运行子程序/用户窗体选项(也可以点击VB窗口菜单下VB标准工具栏中的运行按钮)。
在Excel窗口,点击工具菜单下宏选项中的宏,然后选择MsgBoxDemo宏并运行它,或直接点击VB工具栏中的运行按钮。
用快捷键来运行宏(例如,使用组合键Ctrl+Shift+G),或者为了加深印象,读者可以将宏链接到电子表格的一个按钮上。
运行代码时,如果出现一个运行时错误,运行过程就会停止。一个确定对话框会说明错误的类型,尽管对于新手来说,这种说明不够详细。记住错误信息之后,点击确定按钮,鼠标指示器就会跳到出现错误的语句那里,并且这条语句会以高亮度的形式显示(黄色)。更正错误之后,点击运行菜单下的重新设置(或者点击工具栏中的黑色方框)来去掉黄色亮条,然后重新运行宏。
为了作进一步的练习,可以输入下面改进后的Factorial子程序。在这个版本中,循环语句被用于计算阶乘的Excel函数FACT代替,注意,要在VBA代码中使用Excel函数,只需在FACT函数前加上前缀Application.(或者WorksheetFunction.)就可以了。
Sub Factorial()
'calculates the factorial of a number
num = InputBox("Enter integer number ","Calculate Factorial ")
fac = (num)
MsgBox "Factorial is " & fac
End Sub
为了避免内存溢出,子程序Factorial的输入整数应小于25。
这些简单的宏都是手工输入的。然而,许多操作代码可以用Excel宏录制器来生成,因此下一节我们将介绍这种不同的编写宏的方法。
录制按键和编辑代码
录制器将按键转换成VBA代码,它在解释单元格地址时有两种模式:绝对引用(缺省模式)和相对引用模式。在开始的时候,基本上都是采用绝对引用模式,但在本章后面的附录3B中给出了相对引用的使用细节。为了演示,我们用绝对引用模式录制一个简单的宏,将数据输入到单元格区域B8:C10中。
宏录制器可以在Excel窗体中调用,点击工具菜单下宏中的录制新宏。给宏取一个名称,如Entries,然后点击确定按钮。(也可以点击VB工具栏中的录制宏按钮。)这时会有一个小工具栏出现,里面有一个停止录制按钮(还有一个‘相对引用’按钮)。不要将相对引用按钮按下。
在工作簿中,选中单元格B8并在B8中键入JAN;移动到B9,键入FEB到B9;移动到B10,键入Total到B10。然后到C列,选中C8,键入300;选中C9,键入400;选中C10,在单元格中键入公式‘=SUM(C8:C9)’,并点击停止录制按钮。点击VBE按钮,在代码窗口查看刚才按键的录制代码。它应该与下面这段代码相似:
Sub Entries()
'entering data using absolute addresses
Range("B8").Select
= "JAN"
Range("B9").Select
= "FEB"
Range("B10").Select
= "Total"
Range("C8").Select
= "300"
Range("C9").Select
= "400"
Range("C10").Select
= "=SUM(R[-2]C:R[-1]C)"
Range("C11").Select
End Sub
当一个单元格被‘选中’,它就变成了ActiveCell(Application对象的一个属性,返回一个区域)。Entries子程序用属性将输入值输入到B8至C10中。现在,切换到Excel窗体,点击运行宏按钮(或者在代码窗口,执行菜单操作运行下的运行子程序/用户窗体)运行宏时,可以发现宏的执行结果与前面的相同,也就是说,它也是将数据填写到区域B8至C10中。
作为一个VBA新手,最好的方法是先用宏录制器录制代码,然后编辑改进代码,使之变得更加简洁,然后对代码进行归纳总结。录制、编辑并测试代码是一种很好的练习方法。录制代码的运行结果与录制时执行的一系列菜单操作的结果完全一致,如生成数据图表、进行数据排序、定制枢轴表(pivot tables)等。录制器能够为一些复杂的指令生成简洁的代码。但是,如果录制的操作中包含大量的鼠标移动,则录制器会生成相当冗长的代码。在这个时候就需要手工编写代码,或者先录制,然后编辑。
为了演示,我们回过头来看一看生成的Entries宏代码。录制的代码中包含了许多‘选中’单元格的操作,但在编写代码时,这些操作是没有必要的,可以直接将数值赋给指定的单元格。下面的代码经过编辑,可以执行同样的操作,但代码就显得简洁多了:
Sub Entries1()
'entering data using absolute addresses
Range("B8").Formula= "JAN"
Range("B9").Formula = "FEB"
Range("B10").Formula = "Total"
Range("C8").Formula = "300"
Range("C9").Formula = "400"
Range("C10").Formula = "=SUM(C8:C9)"
End Sub
实际上对于单个单元格区域对象,指定Formula属性是不必要的,所以代码可以简单地写为:
Sub Entries1()
'entering data using absolute addresses
Range("B8") = "JAN"
Range("B9") = "FEB"
Range("B10") = "Total"
Range("C8") = "300"
Range("C9") = "400"
Range("C10") = "=SUM(C8:C9)"
End Sub
整理代码的一个可行方法是将冗余的语句变为注释(在前面加上一个单引号),然后运行宏看看效果。
在编写代码时,经常需要显示一些运行过程中产生的信息,特别是计算的中间结果等。此时,VBA函数MsgBox就非常有用了。在编码阶段,增加一些简单的注释,用来描述宏的行为,以及任何特性或要求等等,是很有用的。更重要的是,应该给出代码可能的应用范围。
作为参考,本节讨论的所有宏都放在ModuleA模块中,可以在VBSUB工作簿的IntroEX表中运行它们。
分析前面提到的Factorial子程序可以得出,那些使用变量并采用For…Next循环语句的代码是不能通过录制按键生成的,必须手工输入。因此,接下来的章节将讨论这一类更加常规的编程方法。
编程要素
金融建模时,要编写的许多代码都是对数值计算进行控制。这包括对它赋值,采用适当的结构来控制过程流,以及报告结果等。这些任务对所有的编程语言来说,都是最普通的。VBA程序的唯一不同之处就在于,它经常需要从电子表格模型开始,其任务是由菜单操作执行,而且关键的计算放在单元格公式中,许多公式还包含Excel函数。幸运的是,将Excel函数嵌入到VBA代码中是很容易的事。VBA程序不是用来代替电子表格模型的,而是用来扩展和增强它们的功能的。我们先来简要回顾一下关于变量的应用(特别是数组变量)、控制过程流的结构,以及VBA中Excel函数的应用等相关内容。
变量和数据类型
编程时,变量是用来存储和操作数据的。按照惯例,数据具有不同的形式或者说‘类型’,而这会影响到它们在内存中的存储空间。在大部分的数值计算中,主要有四种类型的数据:小数型(传统的十进制小数,也叫标量)、整型、逻辑型(True或False)和日期型。而字符型变量存储的是文本信息。VBA有一个非常有用的数据类型,variant,它可以代表任何数据类型。它是缺省的数据类型,当我们不知道一个变量的类型时它是很有用的。但是,variant类型会占用额外的内存空间,这在处理大量数值计算时会影响运行的速度。还有一个‘对象’型数据类型,它可以引用一个对象,如一个区域或者工作表等。更详细的内容见Green(1999)。
从VBA编程新手的角度来看,明确地声明所有需要用到的变量是很重要的,这可以减少代码中错误的出现。你可以在每个模块页的开头写上‘Option Explicit’语句来强制实行这一点。(VBE可以自动地增加该语句,在VB窗口点击 工具 菜单下的选项,然后选中要求变量声明的选项。)这样,在运行时,如果编辑器遇到一个未声明的变量,就会返回一个错误信息。这在检测代码中是否有错误拼写的变量名时显得特别有用。金融建模时,声明变量的类型(作为整型、字符串型还是逻辑型等)并不重要。但它可以用来在程序中说明变量的用途。
变量声明使用Dim关键词,如下面Factorial子程序中的第三行所示。这里,声明了两个变量,但没有给出数据类型(如整型)。缺省情况下,它们是variant类型:
Sub Factorial()
'calculates the factorial of a number
Dim fac,num
num = InputBox("Enter number ","Calculate Factorial ")
fac = (num)
MsgBox "Factorial is " & fac
End Sub
(顺便提一句,注意这里的InputBox()函数,它返回用户的输入信息,在这里它有两个参数,第一个参数是提示信息‘Enter number ’,而第二个参数(可选)则是对话框的标题。)
总体来讲,子程序中用到的所有变量都应该用Dim关键词作显式声明。模块页前的‘Option Explicit’语句保证所有的变量必须预先声明,否则,子程序就不能正确运行。
VBA数组变量
有时变量可以按名称分组组成数组(向量或者矩阵)。例如,一个数据集累积分布的所有四分位数可以用一个数组变量qvec()表示。数组中的元素可以用下面的方式引用:qvec(0),qvec(1)等。利用前面一章讨论的对数收益数据,如图显示,单元格H21:H25中存放的是用Excel的QUARTILE函数计算出来的四分位数,第一个和最后一个分位数分别是数据集的最小和最大值,低于的数据占整个数据集的25%。因此数组qvec()从单元格区域H21:H25中得到数据,qvec(0)的值为,qvec(1)的值为,如此类推。
Quartiles:四分位数值;Cumulative Frequency from quartiles:四分位点的累积频率;
【参照书中第49页的图】
图 VBSUB工作簿Data表中的对数收益四分位数
扩展一下,数组变量可以是多维的,例如,两维的数组变量PQmat()代表包含一列四分位数和一列分布左百分位数的5×2数组。例如,变量PQmat()可以从5×2的单元格区域H21:I25中得到数据,如图所示。尽管对数组变量的命令没有什么规定,但选择可以区分一维数组(向量)和二维数组(矩阵)的名称还是有帮助的。因此我们分别使用qvec和PQmat作为数组变量名称。
和其他变量一样,数组变量应该在使用之前先声明。缺省情况下,VBA从0开始对数组变量编号。因此如果声明语句为:
Dim qvec(4)
则数组qvec()包含5个元素。扩展一下,如果两维数组PQmat()的声明为:
Dim PQmat(1,4)
则它包含10个元素。
如果希望VBA将1作为最小编号,必须在模块页的开头声明‘Option Base 1’。有了这个声明后,数组变量qvec(4)就只有4个元素:qvec(1), qvec(2) ,qvec(3) 和qvec(4)。实际上,在本书的大部分VBA程序中,数组的基数都设为1。
‘Option Base’设置的作用可以从下面的ArrayBase宏中看出。当给模块顶部的‘Option Base’赋不同的值时,产生的结果也不同。既然VBA数组函数Array()是用来输入真实数组的,就没有必要为数组变量avec指定维数:
Sub ArrayBase()
‘gives b=20 if Option Base is 1;gives b=30 in absence of Option Base statement
Dim avec,b
avec = Array(10,20,30)
b = avec(2)
MsgBox “b is ”& b
End Sub
通常,开始时并不知道数组元素的个数,要根据宏的操作结果而定。这种所谓的‘动态数组’事先没有设定元素个数。它用一对空括号声明,如Dim qvec()或者Dim PQmat()。但是在使用任何无维数数组之前,必须先用ReDim语句告诉VBA这个数组有多少元素。在下面的例子中,名为‘dvec’的区域包含了需要存储到数组变量cvec中的结果。在声明cvec为数组变量之后,它的维数就由dvec区域的元素个数决定,在cvec被使用之前需要使用ReDim语句:
Sub SelectCount()
're-Dim data array cvec
Dim n, cvec()
Sheets("Data").Range("dvec").Select
n =
ReDim cvec(n)
End Sub
操作VBA中的数组要比使用Excel的数组公式(如SUM、SUMPRODUCT以及矩阵函数)复杂的多。有些时候,数组操作可以完全由Excel函数来完成。注意,如果代码中不需要访问数组中的单个元素,数组就可以直接用变量名引用,如qvec,而不用加上它的维数。但是,数组处理经常包括对其中元素的操作,对元素进行操作的最好方法是在一个循环语句中应用数组。在这种情况下,元素用qvec(i)来标识,且一般需要使用Dim qvec()和ReDim语句。要想避免出错,需要非常小心地编码。由Excel构建的VBA数组在编号上有些模糊,特别是在处理行向量和列向量时。为了避免混淆,在将一个数组作为过程的输入时,Excel一般对数组元素从1开始编号。
控制结构
和其他编程语言一样,VBA也提供了一些控制流程的结构。这些包括条件语句,如If…Then…Else,它先判断条件是否满足,然后根据判断的结果改变执行的流程;还有Select Case,它从多个条件(Case)中选择一个分支。循环结构允许重复运行一段执行语句。有些循环重复执行特定的次数,如带有计数器的For…Next 循环,前面,我们在计算阶乘时用到过它。其他的语句,如Do While…Loop,重复执行语句直到条件为True(或直到条件为False)。本节先看一个关于If…Then的简单例子,更多的关于For…Next循环语句和Do While…Loop语句的例子会在接下来的章节出现。很多教科书(如Green,1999;Walkenbach,1999;Wells和Harshbarger,1997)中对这些控制语句都作了更为详细的解释,并给出了一些其他的例子。
在Factorial子程序中我们已经提到,VBA的交互函数InputBox可以返回用户的输入信息。为了使程序更强大,我们应该将用户输入的一些错误进行输入过滤掉更正。一种方法是检查输入是否是数值型,如果不是就丢弃它。下面的代码演示了两个If…Then结构语句的使用。一个是单行的If…Then语句,它没有额外的代码行。这条语句检查numtype变量(VBA函数IsNumeric的结果变量)是否为真,如果为真,就计算阶乘。它的检测语句和执行语句在同一行中。第二个条件语句采用块If…Then形式。如果用户的输入不是数值型,则用户会被警告,并且不进行阶乘计算。当条件语句结束后,有一个End If语句:
Sub Factorial()
'calculates the factorial of a number
' filters out non numeric input
Dim fact, num
num = InputBox("Enter number ", "Calculate Factorial ")
numtype = IsNumeric(num) ‘True或者False
If numtype = ture then fac = (num) ‘单行的If…Then
If numtype = False Then ‘块If…Then End If
MsgBox "Not a number. Try again" 'don't proceed
End If
End Sub
我们将在第四章的用户定义函数中多次用到If…Then…Else语句。
控制重复过程
前面(见)的Factorial子程序演示了一个简单的重复操作,For…Next循环。下面是一个不同的例子,Quartiles子程序。它计算一个数据集(来自VBSUB工作簿Data表中的区域dvec)的四分位数,并在一系列的对话框中将结果一一显示给用户。
Option Base 0
Sub Quartiles()
'displays quartiles of range named 'dvec'
Dim i As Integer
Dim qvec 'to hold quartile
Dim dvec As Variant 'col vec of data
'fill array variable from spreadsheet range
dvec = Worksheets("Data").Range("dvec")
'calculate quartile one at a time & display
For i = 0 To 4
qvec = (dvec, i)
MsgBox "Quartile no. " & i & " has value " & qvec
Next i
End Sub
查看一下VBSUB工作簿中的区域名称,通过点击 插入 菜单下名称中的定义选项,我们可以看到,名称dvec是与Data表中E列的LnReturns数据区区域绑定的。在Quartiles子程序的代码中,我们也使用dvec作为数据集数组的变量名。为了表明dvec是一个向量而不是标量,我们将它声明为Variant型。检查代码,我们发现,在用工作表单元格的数据填充完数组dvec之后,每个四分位数都被计算出来,并且用For…Next循环将其一个个地显示给了用户。注意,在VBA代码中使用Excel函数QUARTILE时,需要在前面加上Application.或者是WorksheetFunction.,我们将在下一节解释其中的原因。
MsgBox的提示信息中含有For…Next的计数值以及当前的四分位数,这种办法可行的,但并不是处理输出的最好方法。
另一种可进行重复操作的语句是Do While…Loop。它会重复执行操作,直到条件满足。DelDuplicates子程序是用来查找并删除一个数据表中的重复行,它用于演示Do While…Loop语句的使用方法。将数据信息存储到一个特定的‘code’区域之后,程序会对code列中的数据一行行地作处理。如果currentCell中的数值与nextCell中的相同,则删除currentCell行。这里currentCell和nextCell都是对象变量,因为它们代表的是对象(这里是Range对象)。由于它们是对象变量,因此它们必须用关键词Set来赋值。
Sub DelDuplicates()
Dim currentCell,nextCell
Range(“database”).Sort key1:=Range(“code”)
Set currentCell = Range(“code”)
Do While Not IsEmpty(currentCell)
Set nextCell = (1,0)
If = Then
End If
Set currentCell = nextCell
Loop
End Sub
这个子程序还展示了VBA在线帮助的一些优势。在线帮助可以在VBE中访问,点击帮助菜单中的帮助索引即可。在帮助索引的Delete方法那一段中就含有上面的这段代码,并对它进行了详细的解释。不幸的是,并不是所有的帮助信息都这样详细,但不管怎样,在线帮助中有许多有用的关于语句方面的信息。这几节中的内容只涉及到一些最常用的控制结构语句。有关VBA编程技术还可以参看Green(1999),Walkenbach(1999)以及Wells与Harshbarger(1997)编著的教科书。
在代码中使用Excel和VBA函数
从Quartiles子程序的代码中我们知道,如果想在VBA程序中调用一个Excel函数,需要在函数名的前面加上Application.。在Excel2000中,函数名的前缀可以是WorksheetFunction或者Application。为了与Excel以前的版本兼容,我们继续使用Application前缀。相比而言,调用VBA函数就不需要任何前缀,如我们已经看到的MsgBox、IsNumeric、InputBox等函数。实际上,VBA中也有少量数值函数,目前只有Abs, Cos, Exp,Int,Log,Rnd,Sgn,Sin,Sqr和Tan,其中Log是自然对数函数(通常表示为Ln)。因此,相同功能的VBA函数名的拼写与Excel函数名的拼写是不同的(例如,VBA中的平方根函数是Sqr,而在Excel中是SQRT)。为了解决这种矛盾,我们规定,如果VBA和Excel函数能完成相同的计算时,必须使用VBA函数而不用Excel函数。(例如,计算一些数值的自然对数时,调用Log而不是;同样地,调用Sqr而不是。)
编程的一般观点
结束这一节之前,我们对编程过程提出一些建议。采用结构化编程的目的是使程序按一个有序的方式运行,这样编码就很容易进行,更重要的是,很容易修改。
先在纸上设计出整个应用程序的主要阶段,并将整个任务分成几个不同的子任务。然后把每个子任务再分成一系列子程序,并对它们分别进行编码和测试。如果可能,子程序应保持合适的大小。结构化编程的一个原则是,代码段应该只有一个入口和一个出口。程序控制不能在代码段的中间跳入或跳出。如果坚持这个原则,将各个分开的代码段连接起来就会很简单。
程序经常需要更新和修改。如果在第一次编程时加上注释,那么搞清程序的逻辑就会容易的多。只要可能,数值计算中的重要常量都应该用参数来表示(显式声明),这样,当程序用于不同的环境时,它们就很容易修改了。如果编写的代码能够通用,这些额外的工作是值得的。
宏与电子表格之间的通信
介绍完编程中变量和控制结构的使用之后,本节讨论一下VBA宏怎样从电子表格中获得输入信息,并将结果输出到电子表格中去。其重点在于宏与电子表格之间的通信。
在许多案例中,一个子程序可分为三个部分:数据输入,计算(或者操作输入),然后输出结果。写一个用于计算的VBA代码通常涉及到传统的编程技术。VBA编程的新观点是与电子表格之间的互动。可将这三个部分分开考虑:
输入值可以从电子表格的单元格中读取得到,或用户通过对话框直接输入,输入值存储在变量中。
输出值可以写入单元格,或者用对话框显示给用户。
计算可以用代码来完成(‘离线(offline)’),也可以使用单元格中已存在的,或是用VBA子程序写入单元格中的公式。
接下来的三个大家已经很熟悉的Factorial例子演示了输入、计算和输出的各种不同组合。子程序Factorial1从用户那里得到输入,计算时使用了Excel函数FACT,并且用MsgBox函数来返回结果。整个子程序不与电子表交互:
Sub Factorial1()
Dim fac, num
num = InputBox("Enter number ", "Calculate Factorial ")
fac = (num)
MsgBox "Factorial is " & fac
End Sub
作为对比,Factorial2从电子表格的单元格(B5)中得到输入信息,然后将计算出的阶乘返回给另一个单元格(C5):
Sub Factorial2()
'gets number from spreadsheet, uses Excel fact function, returns answer to spreadsheet
Dim fac, num
num = ("B5").Value
fac = (num)
("C5").Value = fac
End Sub
而在子程序Factorial3中则作了进一步的修改,将用户的输入值写入一个单元格(B6),然后将阶乘公式写入相邻的另一个单元格:
("C6").Formula = "=fact(b6)"
并将结果显示给用户。
Sub Factorial3()
'gets number from InputBox, calculates factorial in spreadsheet
' returns answer via MsgBox
Dim fac, num
num = InputBox("Enter number ", "Calculate Factorial ")
Range("B6").Value = num
Range("C6").Formula = "=fact(b6)"
fac = Range("c6").Value
MsgBox "Factorial is " & fac
End Sub
为了巩固所学的知识,读者可以根据自己的要求进一步改写这些输入——输出子程序。作为参考,VBSUB工作簿的ModuleF模块中包含了所有这些阶乘子程序的代码,并且在IntroEx表中运行良好。改写或者编写子程序前,应该先看看附录3A中关于Visual Basic编辑器的内容,特别是宏编程步骤和调试部分。
当不需要进行单元格对单元格(cell-by-cell)的计算时,读取电子表格区域中的数值并将结果写入其他单元格或区域的过程都是很简单的。假设输入数据在电子表格区域avec中,要将这些单元格中的数据拷贝到另一个区域中,这个区域左上方的单元格名称为aoutput。录制器生成的代码在子程序ReadWrite1中,我们对它进行修改,就变成了ReadWrite2中的样子:
Sub AReadWrite1() ' Macro recorded
Reference:="Avec"
Reference:="Aoutput"
Paste:=xlValues, Operation:=xlNone, SkipBlanks:= _
False, Transpose:=False
= False
End Sub
Sub AReadWrite2() ' edited version
‘reads data,writes values to another range
Range("Avec").Copy
Range("Aoutput").PasteSpecial Paste:=xlValues
= False ‘cancels Copy mode
End Sub
如果计算过程需要对区域avec中的单个单元格进行操作,代码就会变得有些复杂。这些计算可能需要在一个循环体中进行,并且计算结果需要一个一个地拷贝到输出区域中。我们先计算区域avec的单元格个数,这样就可以知道需要从avec中读取多少个单元格数值,也知道需要拷贝多少个数值给输出区域。假设我们要计算avec区域中各个单元格数据的连乘。在计算中,需要用到两个变量x和y,x是avec的当前元素值,而y是当前的连乘值。假设输入区域avec的顶部单元格命名为‘atop’,而输出区域的顶部单元格命名为‘aoutput’。下面的子程序先一个一个地读出单元格数据,然后计算乘积,最后输出当前的乘积:
Sub AReadWrite3()
'reads data from range avec, calculates
'running product & writes results to range below aoutput
Dim i As Integer, niter As Integer
Dim x, y
y = 1
niter = Range("avec").Count
For i = 1 To niter
x = Range("atop").Offset(i - 1, 0)
y = y * x 'calculation routine
Range("aoutput").Offset(i - 1, 0) = y
Next i
End Sub
Offset(i,j)是一个非常有用的对象方法,它返回参考单元格(这里是Range(“stop”))向下i行,向右j列的单元格数据。例如,Range("atop").Offset(1, 0)代表的是单元格atop正下方的单元格,等等。x的第一个赋值是Range("atop").Offset(0, 0),也就是Range("atop")中的值。
作为宏与电子表之间通信的最后一个例子,我们来看看Quartiles子程序,改进它的代码使得它以数组的形式返回五个四分位数给电子表格。这个新的子程序Quartiles1,先读取数组qvec()中的五个四分位数,然后将它们输出到前面已命名的区域qvec1中。(区域名称qvec1与电子表格中的区域K20:K24绑定,可以通过点击插入菜单下名称中的定义选项来检查。)
下面的代码中,数组变量qvec(4)可以存储五个数值。输入和输出数组变量:dvec(数据集)和qvec(结果数组),都被声明为Variants类型,这暗示着它们不是简单的标量。由于不需要访问dvec和qvec1中的单个元素,因此声明它们时不用带括号()。在用电子表格区域dvec的数据填充完数组变量dvec之后,数组变量qvec中的每个元素会在一个For…Next循环中一一赋值。缺省情况下,VBA以行的形式在电子表格中建立数组,因此需要使用Excel函数Transpose来将输出数组转换成列向量:
Option Base 0
Sub Quartiles1()
'pastes 4 by 1 col vector of quartiles into range named 'qvec1'
'requires 2 named ranges in spreadsheet, dvec with data & qvec1 for results
Dim i As Integer
Dim qvec(4) 'quartiles array
Dim dvec As Variant 'col vec of data
Dim qvec1 As Variant 'results array
'fill array variable from spreadsheet range
dvec = Worksheets("Data").Range("dvec")
'calculate quartiles & assemble as col vector
For i = 0 To 4
qvec(i) = (dvec, i)
Next i
qvec1 = (qvec)
' transfer results into spreadsheet range qvec1
Worksheets("Data").Range("qvec1") = qvec1
End Sub
注意,在循环体中,只有一条语句执行。这里要说明一点,在循环体内不要放任何不必要的语句。循环体中任何与循环操作无关的语句都应该放在循环体外。
读者可以用自己的数据和公式来改写一下上面的这些代码,从而巩固所学的知识。作为参考,VBSUB工作表在ModuleI和ModuleQ模块中分别提供了ReadWrite和Quartiles子程序的代码。
本节对编写VBA子程序做了一个简单的介绍。要想加强这方面的知识,可以参看Green(1999)教科书的第2章,它是一本很好的VBA初级读本。下一节将介绍三个更深入一层的VBA应用实例,代码和解释都会更长。它们代表了宏的一些实际应用,并且演示了VBA编程的一些深层思想。
子程序实例
在本节中,我们进一步改写了一些子程序,一来可以演示改进代码的过程,二来可以展示宏的一些应用领域。在编写这些例子时,我们将录制器和代码编辑混合起来使用。这些子程序包括:生成特殊类型的图表,累积分布和正态分布作图,求解的重复优化。
图表
首先,我们改写一个子程序,用来对一些数据(如图中列出的累积频率数据)作图。
假设频率数据放在一个工作簿的Data表中,数据区域命名为‘chartdata’,我们的目的是生成一个基于累积百分比频率分布的XY散点图。图表在Data表中作为一个对象来处理。
Frequency Distribution:频率分布;Cumulative Frequency:累积频率
【参照书中第57页的图】
图 VBSUB工作簿Data表中的频率分布以及图表
对于一个VBA新手来说,他们并不知道操作图表所需的代码,因此最好是先用录制器来生成代码。打开VBA录制器,然后录制对区域‘chartdata’绘图的按键。在任何可能的情况下,都应该用区域名称来表示单元格区域。录制的名为ChartNew()的宏代码如下:
Sub ChartNew()
= xlXYScatterSmoothNoMarkers ‘WizStep1
Source:=Sheets("Data").Range("chartdata"), PlotBy:= _
xlColumns ‘WizStep2
Where:= xlLocationAsObject,Name:=”Data” ‘WizStep4
With ActiveChart ‘WizStep3
.HasTitle = True ‘Titles
. = "Cumulative Distribution"
.Axes(xlCategory, xlPrimary).HasTitle = False
.Axes(xlValue, xlPrimary).HasTitle = True
.Axes(xlValue, xlPrimary). = "P(X>x)"
End With
With (xlCategory) ‘Gridlines
.HasMajorGridlines = False
.HasMinorGridlines = False
End With
With (xlValue)
.HasMajorGridlines = False
.HasMinorGridlines = False
End With
= False ‘Legend
End Sub
(右边的注释是为了解释这段有点长的代码。WizStep1是Chart Wizard Step 1的简称,如此类推。)
代码中Charts集合增加了一个新的图表【】,这个图表的类型为XYScatterSmoothNoMarkers【】,作为数据源的单元格区域为‘chartdata’【】。这段代码是由图表向导的前两个步骤生成的。在步骤一中,图表类型被选中,而在步骤二中,定义了作为数据源的数据区域,并指定了它的结构(行或列)。【对象ActiveChart的SetSourceData方法使用语法:SetSourceData(Source,PlotBy)。】注意,由于语句过长,有一部分放在下一行,这时需要在这个代码行的后面跟一个空格符和一个下划线(_)。
代码行: Where:=xlLocationAsObject,Name:=”Data”由图表向导的第四步产生,它确保新的图表将嵌入Data表内。(如果要将图表放在一个分开的图表页中,可以将代码改为: Where:=xlLocationAsNewSheet。)下面的大部分代码由图表向导的第三步产生,它设定图表选项。注意这一条语句:
‘With…End With’
当对一个对象作某些改变时,Excel录制器经常使用这条语句,在这里是ActiveChart对象。因此,下面的代码:
With ActiveChart
.HasTitle = True
. = "Cumulative Distribution"
.Axes(xlCategory, xlPrimary).HasTitle = False
.Axes(xlValue, xlPrimary).HasTitle = True
.Axes(xlValue, xlPrimary). = "P(X>x)"
End With
定义了图表的标题,而接下来的代码段则分别定义了图表的网格、坐标轴和图例。
录制器生成的许多代码没有改变缺省值,因此这部分代码可以删除,从而使宏看起来更加简洁。第一步是注释掉可能的冗余语句(在语句的前面加一个单引号),然后检查一下代码是否仍然象你希望的那样运行。例如,下面的这段代码中,在去掉注释行后ChartNew1子程序仍能很好地运行:
Sub ChartNew1()
= xlXYScatterSmoothNoMarkers
Source:=Sheets("Data").Range("chartdata"), PlotBy:= _
xlColumns
Where:= xlLocationAsObject,Name:=”Data”
With ActiveChart
.HasTitle = True
. = "Cumulative Distribution"
.Axes(xlCategory, xlPrimary).HasTitle = False
.Axes(xlValue, xlPrimary).HasTitle = True
.Axes(xlValue, xlPrimary). = "P(X>x)"
End With
‘With (xlCategory)
‘ .HasMajorGridlines = False
‘ .HasMinorGridlines = False
‘End With
With (xlValue)
.HasMajorGridlines = False
.HasMinorGridlines = False
End With
= False
End Sub
因此,这段代码可以精简为:
Sub ChartNew2()
' Same as ChartNew Macro after removing redundant lines
= xlXYScatterSmoothNoMarkers
Source:=Sheets("Data").Range("chartdata"), PlotBy:= _
xlColumns
Where:=xlLocationAsObject, Name:="Data"
With ActiveChart
.HasTitle = True
. = "Cumulative Frequency"
.Axes(xlValue, xlPrimary).HasTitle = True
.Axes(xlValue, xlPrimary). = "Freq(X>x)"
End With
With (xlValue)
.HasMajorGridlines = False
End With
= False
= xlAutomatic
(xlValue).Select
With (xlValue)
.MaximumScale = 1
End With
End Sub
代码及其之后的代码是后加的。这段代码设置坐标轴y的最大值为1(或100%),并且将绘图区的缺省背景填充色灰色去掉。
前面提到的这些宏在工作簿的ModuleC模块中定义,并能在Data表中运行。
正态概率散点图
正态概率散点图是一种有用的数据可视化工具。绘出的图形可以表明观测变量是否可以假设服从正态分布。假设有50个观测值。我们先计算出服从正态分布的50个顺序值(叫做norm-scores),并用它们对照50个观测值的标准化值(叫做z-scores)绘图。如果观测值是服从正态分布的,则两组值绘出的点大致在一条直线上。图中绘出的散点表明,观测值与正态分布有一点偏差,特别是在分布的尾部。
现在,Excel的统计库中已经步包括这种有用的绘图工具了。(数据分析工具库回归选项中的正态概率分布图并不是传统意义上的正态概率散点图,它只是应变量的累积分布图。)
Normal Probability Plot:正态概率散点图;
Norm-score:正态值;z-score:z-值;
【参照书中第60页的图】
图 对数收益数据的正态分布散点图
假设观测值放在Data表的名为dvec的区域中。在VBA代码中,数组变量dvec用于存储单元格区域dvec中的数据。由于我们需要对数组元素dvec(i)进行处理,因此需将dvec声明为Variant型(它是一个真正的对象变量,确切地说是一个Range对象变量)。第二个变量,Znvec,用于存储向量z_scores和norm-scores,其中的一列存储dvec中的值。这些变量在子程序NPPlotData的开头部分声明。
既然变量dvec的值来自于单元格区域,因此它需要用关键词Set来赋值,因此:
Set dvec = Sheets("Data").Range("dvec")
一旦dvec被赋值,dvec中的观测值个数就可以计算出来,那么前面定义的数组维数就可以确定了。因此有下面这条语句:
ReDim Znmat(n, 2)
对数据集中的每一个观测点,它的z-score和norm-score在一个For…Next循环中计算出来。计算z-score时要用到Excel的AVERAGE函数和STDEV函数,而计算norm-score时则要用到Excel的RANK函数和标准正态分布的逆函数(NORMSINV)。计算出来的值放在一个n×2的矩阵Znvec中,然后按顺序输出到电子表格的单元格区域Znvec中。一旦数值计算完毕,就可以用它们绘出一个XY散点图。
下面是计算并绘制散点图的全部代码。代码的倒数第二条语句是调用子程序ChartNormPlot,这个子程序与节中的图表子程序有些相似。你可以在VBSUB工作簿的Data表中运行NPPlot宏。
Option Explicit
Option Base 1
Sub NPPlotData()
' returns n by 2 matrix where n = no. of elements in dvec
' 1st col z-scores, 2nd col normscores for n obs.
Dim M, V, r, c
Dim i As Integer, n As Integer
Dim Znmat() As Variant
Dim dvec As Range
'data input from worksheet
Set dvec = Sheets("Data").Range("dvec") 'dvec as Range object
n = (dvec)
ReDim Znmat(n, 2)
M = (dvec)
V = (dvec)
For i = 1 To n
Znmat(i, 1) = (dvec(i) - M) / Sqr(V)
r = (dvec(i), dvec, 1)
c = (r - 3 / 8) / (n + 1 / 4)
Znmat(i, 2) = (c)
Next i
'output results to range Znmat
Sheets("Data").Range("Znmat") = Znmat
With Sheets("Data").Range("Znmat")
.NumberFormat = ""
.Name = "npdata"
End With
ChartNormPlot
End Sub
子程序NPPlot和ChartNormPlot(在NPPlot中被调用)的代码放在工作簿的ModuleN模块页中。
用规划求解产生有效边界
这个应用程序要求先对投资组合理论以及Excel插件规划求解有一定的了解。读者可以等到学完第六章之后再来看这些内容。
在包含多个风险资产的投资组合中,我们需要求出有效组合的资产权重,也就是求出能使得指定收益下风险最小的资产权重。可以直接用规划求解来求解特定收益的最优权重问题。如果对不同的收益反复求解这个最优化问题,就可以得到有效组合的风险——收益前沿边界,也就是所谓的有效边界。
例如,图中给出了组合的三种资产权重分别为40%、50%、10%时,组合的期望收益为%。而目标收益是7%,因此所求得的有效组合权重必须使得组合的收益为7%,且组合收益的标准差最小。
Asset Data:资产数据;
Correlation Matrix:相关系数矩阵;
VCV matrix:VCV矩阵;Portfolio weights:组合权重;
【参照书中第62页的图】
图 Eff1表中组合的权重、期望收益以及收益的标准差
为了解决这个最优化问题,规划求解需要设定可变单元格,求最小值或最大值的目标单元格,以及限制条件说明,限制条件用来限制可变单元格中数据的变化范围。在图中,我们要求单元格I10:I12中的权重之和为1,因此,单元格I10中的公式写成‘=1-SUM(I11:I12)’的形式。为了进行最优化求解,可变单元格选择I11:I12,它们在表中命名为‘change1’。要求最小化的目标单元格是名为‘portsd1’的收益标准差(I16)。这里有一个明显的限制条件:名为‘portret1’的期望收益(单元格I15)必须等于目标收益(在I5中,命名为‘target1’)。代码中要用到的一些区域名称都显示在电子表格中。
执行完一次规划求解,建议购买的Bonds、Shares以及出售的TBills的最优化比例就显示在图中。所求的有效组合在保证期望收益为7%的情况下,它的最小标准差为%。改变I5中的目标期望收益,保持设置不变,再执行一遍规划求解,就可以得到另一个有效组合。很明显,如果想得到整个有效边界,最好的办法是写一个宏。如果录制上面的最优化操作,就可以得到下面的代码:
Sub TrialMacro()
SolverReset
SolverOk SetCell:="$I$16", MaxMinVal:=2, ValueOf:="0", ByChange:="$I$11:$I$12"
SolverAdd CellRef:="$I$15", Relation:=2, FormulaText:="target1"
SolverOk SetCell:="$I$16", MaxMinVal:=2, ValueOf:="0", ByChange:="$I$11:$I$12"
SolverSolve
End Sub
Efficient Frontier points using Solver(no constraints on weights):
利用规划求解计算出的有效边界点(权重无约束情况下)
Asset Data:资产数据;
Correlation Matrix:相关系数矩阵;
VCV matrix:VCV矩阵;
Efficient frontier portfolio:有效边界组合;
【参照书中第63页的图】
图 规划求解出来的期望收益为7%的最优组合权重
录制出的代码中含有一些规划求解插件中的特殊VBA函数。特别是SolverAdd函数(它设定一个限制条件),它有三个参数:第一个表示限制条件左侧的单元格引用,第二个表示操作符的整型代码(2表示‘=’),第三个要么是一个单元格引用,要么是一个数值。类似的,SolverOK函数的参数指定了需要解决的特殊优化问题。
要改进录制的代码,首先可以写出函数名,并在后面的括号里写上函数所需的参数,参数之间用逗号隔开,然后使用区域名称来引用单元格地址。用在每一个Solver函数前的关键词Call表明控制已经转到一个函数过程中。(同时,它也禁用了函数的返回值。)编辑之后,优化的代码如下:
Sub Eff0()
' edited version of Eff0()
SolverReset
Call SolverAdd(Range("portret1"), 2, Range("target1"))
Call SolverOk(Range("portsd1"), 2, 0, Range("change1"))
Call SolverSolve(True)
SolverFinish
End Sub
SolverAdd函数能够确保期望收益(在名为‘portret1’的单元格中)等于单元格‘target1’中设定的目标收益。而SolverOk函数控制最优化求解,保证单元格区域‘change1’中的数值能够使得组合的标准差最小。SolverFinish函数的作用相当于在求解之后,在求解结果对话框中选择选项,并点击确定按钮。有了这个函数,求解结果对话框在宏的控制下不会出现。
要得到有效边界,需要对一系列目标收益求出最优组合权重。假设这些目标收益从初值1%(用min_tgt表示)开始,并以2%(incr)递增。不同的目标收益在一个Do While…Loop循环中产生,循环次数为niter。对于每一个目标收益,Solver都求出相应的最优权重。每一次循环中唯一的变化是目标收益。这样,在代码中,大部分规划求解设置都可以放在循环体之外。只有用来改变目标收益的SolverChange函数需要放在循环体内。
Sub Eff1()
' repeated optimisation with given min_target & increment
' initialisation
Dim target1 As Double
Dim incr As Single
Dim i As Integer, niter As Integer
target1 = Range("min_tgt1").Value
incr = Range("incr1").Value
niter = Range("niter1").Value
' clearout previous results
Reference:="storestart1"
If = 1 Then GoTo SetUpSolver 'special case
(xlCellTypeConstants, 1).Select
'set up Solver
SetUpSolver: 'for special case
If = 1 Then MsgBox ("Setting Up Solver. Continue ?")
Reference:="storestart1"
SolverReset
Call SolverAdd(Range("portret1"), 2, Range("target1"))
Call SolverOk(Range("portsd1"), 2, 0, Range("change1"))
' repeated part
' = False
For i = 1 To niter
Range("target1").Value = target1
Call SolverChange(Range("portret1"), 2, Range("target1"))
Call SolverSolve(True)
SolverFinish
Range("target1").Copy
Range("storestart1").Offset(0, i).PasteSpecial Paste:=xlValues
= "%"
Range("portwts1").Copy
Range("storestart1").Offset(1, i).PasteSpecial Paste:=xlValues
= "%"
target1 = target1 + incr
Next i
Range("target1").Select
= False
= True
End Sub
编写完代码后,就可以为这个宏增加一些描述,并且分配一个快捷键,就像录制宏时的那样。可以在Excel窗口中点击 工具 菜单下宏中的宏选项。在宏对话框中,选中宏的名称(Eff1),然后点击选项按钮。
作为参考,计算有效边界的宏代码放在VBSUB工作簿的ModuleS模块页中,并可以在Eff1表中运行。在调用任何包含规划求解的宏之前,必须先建立一个对规划求解插件的引用。保持Visual Basic模块在激活状态,然后点击工具菜单中的引用,然后浏览并找到(通常在\Office\Library目录下)。
小结
VBA是一种面向对象的Basic编程语言。与传统的编程思想一样,这种语言不但有变量和编码,在使用Excel对象时,还有方法和属性。
首先需要在工作簿的一个模块页中编写子程序和函数,然后才能运行宏来进行自动化操作。Visual Basic环境提供了调试宏和访问VBA库以及在线帮助的工具。
VBA子程序的目的是执行动作,相比之下,VBA用户定义函数则是为了返回数值。在进行金融建模时,我们发现函数比子程序更有用。除非是执行生成图表这一类的操作,或者完成利用规划求解来进行自动优化的任务。子程序在进行一次性(one-off)管理任务时经常用到,这种任务往往需要进行多次重复。
宏录制器可以用来将动作转换成VBA代码。录制按键有时在开始阶段很有用,可以用来得到代码提示。但录制器往往会为一个简单的操作生成冗长的代码,它会由于许多菜单操作生成非常复杂的代码。
多数Excel函数可以用在VBA宏中,也有一些特殊的VBA函数在宏中被调用。这是衡量一个有经验的Excel模型构建者水平的有效途径。
掌握VBA后会带来很多好处。用函数来代替一些计算可以增强单元格公式的功能,并使计算过程更加强大和快捷。熟练地使用宏来控制重复操作,可以减少很多电子表格操作带来的错误。
参考文献
Green,J.,1999,Excel 2000 VBA Programmer’s Reference,Vrox Press Ltd.,Birmingham.
Leonhard,W., and ,1997,Excel 97 Annoyances, O’Reilly&Associates, Inc., Sebastopol, CA.
Walkenbach,J.,1999,Excel 2000 Power Programming with VBA,IDG Books,Foster City,CA
Wells,E. and S. Harshbarger,1997,Nicrosoft Excel 97 Developer’s Handbook,Microsoft Press.
附录3A Visual Basic编辑器
Excel 97(及其以后的版本)对Visual Basic编辑器作了大量的改动,此附录将详细介绍它的一些特性。可以点击VBE按钮(在VB工具栏中)或在Excel窗体中按下组合键Alt+F11来激活Visual Basic编辑器。一旦激活Visual Basic编辑器之后,你就可以用Alt+Tab组合键在Excel窗体和Visual Basic窗体之间切换。Visual Basic编辑器也可以通过点击Excel窗体的 工具 菜单下宏中的Visual Basic编辑器选项打开。
Visual Basic窗体看起来如图中显示的那样,它的顶部有一个菜单栏和一个工具栏;工程资源管理器和属性窗口在左边,代码窗口在右边。代码窗口中显示的是Module1的代码,它其实就是节中描述的数据输入操作的录制代码。
【参照书中第66页的图】
图 包含工程资源管理器、属性窗口和代码窗口的Visual Basic窗体
第一次激活Visual Basic编辑器时,工具栏和上述的那些窗口可能看不到。如果工具栏看不到,可以单击菜单视图下工具栏中的标准选项。同样地,可以选中视图菜单中的工程资源管理器和属性窗口来显示左侧那两个窗口。要显示代码模块,可以在工程资源管理器窗口中双击想要显示的模块(这里是Module1)
顾名思义,代码模块中包含着VBA程序代码。这些模块可以通过点击菜单条插入中的模块来插入。(注意,是模块而不是类模块,类模块是更高层次的应用,在本书中不作介绍。)在工作表簿中,想插入多少模块就可以插入多少模块。实际应用中,一般将相关的宏放在同一个模块中。
工程资源管理器显示所有已打开‘工程’的组成部分。它主要用来作为一个浏览工具,通过它,用户可以打开新的模块来存储代码,或是删除已有的模块,或是激活已存在的代码模块窗口。选中一个模块页,文件菜单允许移走这个模块页(也就是删除它),也可以打印它里面的内容。如果想从一个模块中拷贝VBA代码到另一个模块,请不要用导出文件选项。从一个模块中拷贝代码到同一个工作簿中的其他模块,最好使用代码窗口中的通用‘拷贝’和‘粘贴’操作。
工程资源管理器中显示的每个特定‘工程’中的每一个对象都有它自己的一组属性,例如,被激活的工作簿中的每一个工作表,都有‘name’属性。这些属性可以通过属性窗口来修改。但是,由于修改属性与模块的关系不大,因此我们在关于宏的内容中不再讲述这个窗口的特性,只要知道每一个模块的名称可以在这里修改就行了。(例如,我们经常将模块的名称改为M、0和1,将所有的宏都收集到ModuleM中,然后将数组基准值为0的函数放在Module0中,将数组基准值为1的函数放在Module1中。)在实际应用中,当编写宏代码时,关掉属性窗口和工程资源管理器,并使代码窗口最大化,这样编写代码就显得更加方便了。
Visual Basic窗体有它自己的标准工具栏,如图所示,其中视图Microsoft Excel按钮在最左边,点击它可以把用户带回到Excel窗体。中间部分的按钮是关于编辑代码、运行和测试宏代码的,而右侧的这组按钮则是用来打开其他窗口的,如立即窗口(用来测试单条语句)和对象浏览器(查找能够在VBA中适用的对象、方法和属性)等。特别需要注意的是运行宏按钮(一个指向右侧的三角形),重新设置按钮(一个正方形)和对象浏览器按钮(从右向左数第三个按钮)。
【参照书中第67页的图】
图 VB窗体中的标准工具栏
用VBA编程时有两种类型的程序:子程序(或宏)和函数。只有子程序(或宏)可以录制。录制后的代码通常需要在代码窗口作进一步的修改,并用Excel调试工具来测试。由于函数不能录制,它们必须在一个代码模块中手工编写。因此录制工具在编写函数时基本无用。
如前所述,子程序可以在Excel窗体中运行(通过菜单操作,或者点击VB工具栏中的运行按钮,或者使用一个快捷键)。它们也可以在代码窗口中运行(将鼠标指示器移动到子程序代码体内的任何地方,然后点击VB窗体菜单下VB标准工具栏中的运行按钮)。相比上面的两种方法而言,如果我们能够将子程序与按钮绑定,也许会对用户更友好。但是,既然我们的工作簿相对来说更依赖函数而不是宏,因此我们在此就不介绍这方面的内容,读者可以参考其他文献来了解它。(例如,可以参看Leonhard et al.,1997中的‘VBE and developing macros’部分。)
注意,运行子程序时,如果VBE中断了它,并告诉存在一个编译错误时,VBE会以一种‘调试模式’挂起宏操作,而此时子程序名会以黄色显示。可以将鼠标移到出错的语句,修改简单的错误,然后点击运行菜单中的重新设置或点击工具栏中的重新设置按钮来跳出‘调试模式’。这时黄色条纹会消失,宏就可以重新运行了。
逐语句运行宏和使用其它调试工具
如果你的宏并不如你所期待的那样运行,采用逐语句运行的方式是很有帮助的。如果你通过菜单操作来运行宏,选择工具菜单下的宏,然后选中宏名称,这时你需要在宏对话框中点击逐语句按钮,而不是运行按钮。这使得你能够逐语句地运行宏。如果鼠标不在代码窗口,你可以点击VBA工具栏中的运行按钮,选中要测试的宏,然后点击逐语句按钮。这时相应的代码窗口就会打开,而且被选中的宏的第一条语句会高亮显示(黄色)。
按下功能键F8(或者逐语句按钮,如果它可见的话)执行语句到下一行。如果你希望看到电子表格窗口中的动作,可以缩小VBE窗口,直到你可以看到窗口下面的Excel工作表,或者使用Alt+Tab组合键在两个窗口之间切换,并且观察代码窗口中宏的逐步执行情况。不断点击F8(或者逐语句按钮)就可以逐语句地运行宏了。有一个跳出按钮可以跳出逐语句执行模式。也可以使用VBE编辑窗口中运行菜单下的重新设置选项,点击它就可以跳出逐语句执行模式或调试模式。
图中的调试工具栏中包含逐语句按钮、逐过程按钮和跳出按钮,它们是用来执行宏代码的。一个宏如果按逐语句的模式来运行,被称为‘断点模式’。如果宏中还调用其他的二级子程序,那么点击逐过程按钮就会直接执行这些二级子程序,而不是跳到二级子程序里面逐语句地执行了。
【参照书中第68页的图】
图 调试工具栏,逐语句、跳出和重新设置按钮分开显示
为了演示,我们先在VB窗体中调出调试工具栏(点击视图下的工具栏),并且试着以逐语句的方式执行Factorial宏(它的代码在VBSUB工作簿的ModuleA模块页中)。首先激活IntroEx表,选择工具菜单下宏中的宏选项,选中Factorial宏,如图所示,然后点击单步执行按钮。这时VB窗口就会出现,并且Factorial宏代码的第一条语句高亮显示(黄色)。点击逐语句按钮继续单步执行。当InputBox要求输入数值时,输入3并点击确定按钮。继续单步执行(检查循环体是否执行了三遍),直到MsgBox对话框出现,并返回结果6。
在任何时候,你都可以点击重新设置按钮(或者点击VBE标准菜单栏运行下的重新设置选项)来跳出单步执行模式。
【参照书中第69页的图】
图 进入Factorial宏的单步执行模式
为了作进一步的演示,我们先点击本地窗口按钮(调试工具栏中从右向左数第五个按钮),然后再次单步执行Factorial宏。在新的观测窗口中,随着宏代码的一步步运行,会依次出现变量num、fac和i的值。
另一个调试宏代码的工具是插入断点,在宏代码很长的时候它非常有用。当运行宏时,一旦遇到断点就会停下来。这时运行进入断点模式,并且宏可以单步执行。要想插入一个断点,先找好代码中的断点位置,然后点击代码左边的灰色边界,或者直接点击断点按钮(图中显示的小手)。要想去掉断点,只需再次点击代码左边的灰色边界,或再次点击断点按钮。在Factorial宏代码中,试着在MsgBox语句处加上断点,然后执行宏代码,看看能否正常执行到断点处。然后按一下F8,将运行模式改为‘单步执行’模式。(断点也可以用来调试函数,因为它提供了一种进入‘单步执行’模式并检测函数代码执行情况的方法。)
附录3B 用‘相对引用’模式来录制按键
录制器将按键转换成VBA代码,它在解释地址时有两种模式:绝对引用(缺省模式)和相对引用模式。在节,录制输入数据到工作表中的宏采用了绝对引用。这里,我们采用相对引用模式来看看生成的录制代码。
将鼠标放在单元格B8中,点击工具菜单下宏中的录制新宏选项,从Excel窗体中调出宏录制器。然后给宏取一个名字,比如RelEntries。这次,当停止录用的按钮出现在屏幕中时,点击‘相对引用’按钮。这个按钮会出现‘按下’状态。然后象上次一样,键入月份的名称。完成录制后,生成的代码看起来与上次不同:
Sub RelEntries()
'recorded with relative positions
= "JAN"
(1, 0).Range("A1").Select
= "FEB"
(1, 0).Range("A1").Select
= "Total"
(-2, 1).Range("A1").Select
= "300"
(1, 0).Range("A1").Select
= "400"
(1, 0).Range("A1").Select
= "=SUM(R[-2]C:R[-1]C)"
(1, 0).Range("A1").Select
End Sub
试着在工作表的不同单元格处运行宏。输入值会在鼠标所在单元格(激活的单元格引用为Range(“A1”))的相对位置出现。它先每次向下移动一行,然后移到相邻的列中。Offset方法,比如Offset(1,0),返回一个Range对象,它处于当前单元格同一列的下一行。选中的单元格总是被引用为“A1”(ActiveCell),而不管按键操作是否包括单元格A1。
为了从录制器中得到更多的帮助,我们需要思考一下,到底绝对引用和相对引用中哪一种模式更有用。通常,我们使用录制器的缺省模式:绝对引用模式。但是,如果在工作表中一行行地重复执行某些操作,采用相对引用模式似乎更方便一些。例如,如果你希望宏先选中一个单元格,执行一个动作,然后移动到当前单元格的一个相对位置,这时应该使用相对引用模式。但是,录制完代码之后,记住再次点击相对引用按钮,将其返回到绝对引用模式。
编辑RelEntries宏使它显得更加简洁。录制的代码中经常包含单元格的‘选中’操作,而这在编写代码时是没有必要的。下面的这段改进后的代码可以执行同样的操作,但显得简洁的多:
Sub ConciseEntries1()
'edited version
(0, 0).FormulaR1C1 = "JAN"
(1, 0).FormulaR1C1 = "FEB"
(2, 0).FormulaR1C1 = "Total"
(0, 1).FormulaR1C1 = "300"
(1, 1).FormulaR1C1 = "400"
(2, 1).FormulaR1C1 = "=SUM(R[-2]C:R[-1]C)"
End Sub
实际上,对于只包含一个单元格的range对象,不需要特别指定Formula属性,因此代码可以更简单:
Sub ConciseEntries1()
'edited version
(0, 0) = "JAN"
(1, 0) = "FEB"
(2, 0) = "Total"
(0, 1) = "300"
(1, 1) = "400"
(2, 1) = "=SUM(R[-2]C:R[-1]C)"
End Sub
一个实用的清理代码的方法是:先注释掉冗余语句(在语句前加上单引号),然后运行宏来测试效果。
第4章 编写VBA用户定义函数
VBA代码不仅可以用于编写电子表格的自动操作程序,还可以用于编写函数,这些函数与Excel内嵌函数的工作方式是一样的。函数在进行重复自动计算时非常有用。函数计算是‘离表(off sheet)’执行的,函数一旦编写完毕,就可以拷贝到不同的工作簿中使用。
VBA子程序通常执行一个或多个动作,而VBA函数则是由一系列指令组成的,它们返回单个数值(如SUM函数)或一个数组(如LINEST函数)。用户定义函数能将编程语句(包括循环和条件选择语句)与Excel函数(如NORMSDIST和MMULT)结合起来。
最简单的函数是单值输入单值输出函数。本章从一个简单的单值输入单值输出销售佣金函数开始,来描述编写和使用函数的步骤。多标量输入函数的编写方式与它基本相同。作为演示,我们将编写基于布莱克-舒尔斯期权定价公式的函数。当输入的信息是数组形式而不是单个数值时,编写函数就显得复杂多了。为了演示如何进行数组处理,我们还将使用一个用来计算一组现金流的期望及方差的例子,和一个计算资产组合方差的例子。还有一些输入和输出都是数组形式的例子。这些函数的代码放在工作簿中,鼓励读者用这些例子来作练习,并练习本章后面的习题。
简单销售佣金函数
假设佣金率依赖于每月的总销售额,佣金率显示在图的单元格D5:E7中。
Sales Commission Function:销售佣金函数
【参照书中第73页的图】
图 工作簿SalesCom表中的销售佣金率
如果单元格A5中的数值表示总销售额,那么佣金(单元格B5中)有三种计算方法,用带有嵌套IF函数的公式表示为:
=IF(AND(A5>=0,A5<1000),A5*,IF(AND(A5>=10000,A5<20000),A5*,A5*))
然而,如果需要频繁地计算销售佣金,而且涉及到更多的佣金率,这时最好是建立一个用户定义函数,它能象Excel其他函数一样被调用。我们先来讨论适用于编写函数的VBA代码,然后在下一节解释怎样编写代码,并在工作簿中测试其结果。
参看下面一个名为Commission的函数代码,它需要一个输入值(或‘参数’),也就是变量名为Sales的月总销售额。函数的调用方式为Commission(Sales),它的输出就是所欠的佣金。此段代码在一个模块页中编写,就像宏一样:
Option Explicit
Function Commission(Sales)
' Calculating sales commission
If Sales >= 0 And Sales < 10000 Then Commission = Sales *
If Sales >= 10000 And Sales < 20000 Then Commission = Sales *
If Sales >= 20000 Then Commission = Sales *
End Function
代码解释如下,首先要定义函数,以关键词Function开始,函数名为Commission,并需要一个输入值,Sales,输入变量放在括号中。执行函数代码时,有一个数值结果被赋给函数名Commission。条件语句If…Then决定适当的佣金率,并用它乘以销售额。这三条语句中只有一条被执行,因此函数值只取决于销售水平。代码以End Function语句结束。注意,以单引号开头的语句是注释语句,它们不被执行。
作为好的编程习惯,模块页开头处的Option Explicit语句能够确保VBA代码中所有变量都必须声明。这里仅有两个变量:Commission和Sales,它们在函数的第一句已被隐式声明了,因此不需要再声明一次。(详细介绍见节。)
在工作表中创建Commission(Sales)函数
在工作表中创建Commission函数时,先首在工作簿中插入一个模块页。点击工具菜单下宏选项中的Visual Basic编辑器,然后进入VB窗口。点击工程资源管理器,然后点击插入菜单下的模块选项,这时会出现一个新的模块页。然后就可以在代码窗口中输入函数代码了(注意将Option Explicit语句写在模块页开头)。
可以在工作表中测试这个函数,输入公式:
=Commission(25000)
其结果应该是3000(因为这一水平销售额的佣金率为12%)。或者使用单元格A5中的数据,使用公式:
=Commission(A5)
象Excel的其他函数一样,粘贴函数按钮也可以用来帮助输入Commission这一类的函数。所有用户创建的函数都被归于用户定义类别。
创建的函数不正确时,单元格中就出现一条错误信息(如#NAME?或#VALUE?),通常表明函数与输入值之间不匹配,或者函数名写错了。Microsoft Visual Basic‘编译错误’出现错误时,表明代码有错误。这时,先记下错误类型(经常是‘Variable not defined’),然后点击确定按钮,并更正高亮显示的错误。然后,点击VB标准工具栏中的重新设置按钮(黑色方框),它会把黄色条纹去掉。返回工作表,重新输入创建的函数(按F2键然后输入)。
另一种检测函数工作状况的方法是在代码中使用断点,并用本地窗口观测计算出的数值。例如,点击语句:
If Sales>=0 And Sales<10000
左侧的边界设置一个断点(如附录3A中的描述)。然后返回SalesCom表重新输入Commission函数。当调用函数时,指针会跳回到函数代码窗口,并且在单步运行余下代码时,可以在本地窗口观测到一系列计算结果。
在离开VBE时,可以在属性窗口中将模块页改名(如附录3A中的描述),如改成Module0。
接下一节中,我们将编写几个稍微复杂一点的函数,它们有多个输入变量,并在代码中调用Excel函数以及VBA函数。下节用户定义的函数是布莱克-舒尔斯期权定价函数。
多参数期权定价函数
Excel至今还没有利用布莱克-舒尔斯公式来计算期权价格的内嵌函数。因此,我们可以编写一个用户定义函数来给看涨期权定价,命名为BSCallValue。布莱克-舒尔斯公式的背景知识将在本书的第三部分介绍。现在不必理解期权定价公式,这里的目的只是将公式变成VBA代码。
一个欧式看涨期权的布莱克-舒尔斯定价公式为:
其中,S表示即期股票价格,X表示看涨期权在T时刻的执行价格,r表示无风险连续复利,因此式子exp(-rT)表示时间段T的无风险折现因子。股票的连续股利收益表示为q,因此股票价格S在定价公式中被Sexp(-qT)代替。符号N(d)用来表示小于d的累积标准正态概率。其中,和为:
是股票的波动率。
图显示了一份6个月到期的股票期权的详细情况:当前价格。和在单元格G8和G11中计算得到,它们的累积正态概率和在单元格G9和G12中使用NORMSDIST函数计算得到。这些都是期权价格单元格公式(G4中)的输入值:
=D4*D16*G9 – D5*D15*G12
其计算结果为。(对应的看跌期权价格显示在单元格H4中。)
看涨期权公式可以用一个用户定义函数表示,它有六个输入值(),其计算结果在单元格G5中显示。
Black-Scholes Formula:布莱克-舒尔斯公式
Call:看涨期权;Put:看跌期权;
【参照书中第76页的图】
图 期权信息与计算出来的布莱克-舒尔斯期权价格
函数BSCallValue(S,X,r,q,tyr,sigma)的VBA代码在下面给出。当前距期权到期日的时间表示为tyr(单位:年),表示为sigma。布莱克-舒尔斯公式的计算相当复杂,因此我们在代码中使用了中间变量【DOne表示,而NDOne表示】:
Option Explicit
Function BSCallValue(S, X, r, q, tyr, sigma)
' Returns Black-Scholes Call Value (allowing for q=div yld)
Dim ert, eqt
Dim DOne, DTwo, NDOne, NDTwo
ert = Exp(-r * tyr) 'exp is the VBA function for 'e'
eqt = Exp(-q * tyr) 'dividend yield effect
DOne = (Log(S / X) + (r - q + * sigma ^ 2) * tyr) / (sigma * Sqr(tyr))
DTwo = (Log(S / X) + (r - q - * sigma ^ 2) * tyr) / (sigma * Sqr(tyr))
NDOne = (DOne)
NDTwo = (DTwo)
BSCallValue = (S * eqt * NDOne - X * ert * NDTwo)
End Function
模块页开头的Option Explicit语句强迫函数中所有的变量都要声明。‘Dim’声明了六个变量(ert,eqt,DOne,DTwo,NDOne,NDTwo)。这些变量的名称都具有描述的性质,如Done表示,NDOne表示,等等,它们将代码与期权公式中的传统符号联系起来。变量ert表示将到期日的收益转换成现值的折现因子:代数形式为exp(-rt)。变量eqt是影响股票当前价格的股利收益,代数形式为exp(-qT)。注意,输入变量(如S,X,等等)在函数命令中已自动声明,因此它们不需要在Dim语句中声明。
这段代码尽管看起来很笨拙,但其计算还是相对简单的。三个表达式‘exp’、‘log’、‘sqr’都是内嵌的VBA函数,分别表示指数、对数和平方根计算。当VBA函数和Excel函数都能进行相同的计算时,必须使用VBA函数而不是Excel函数。在代码中调用Excel函数时,函数名前必须加上Application.或WorksheetFunction.。这里:
NDOne = (DOne)语句调用了Excel函数NORMSDIST。
于是,公式
=BSCallValue(100,95,8%,3%,,20%)
的计算结果为。(单元格G5中的公式将D列中的单元格数值作为输入,并显示结果为。)
考虑一份欧式看跌期权的BS定价公式,由于看涨期权和看跌期权是对偶关系,所以欧式看跌期权的定价公式如下:
EMBED
其中,和与前面的一样。它与看涨期权公式的唯一区别在于:和前面的正号变成了负号,并且布莱克-舒尔斯公式中的两项都变了号。增加一个参数iopt(当值为1时计算看涨期权价格,当值为-1时计算看跌期权价格),前面计算看涨期权价格的VBA代码就可以改写成用来计算两种欧式期权价格,看涨期权或看跌期权。
BSOptValue函数的代码在下面给出,它有七个输入变量(包括iopt,其值为1或-1)。如果iopt = -1,则NDOne变为,NDTwo变为,并且BS方程中两项表达式前的符号也同时改变。在工作表中测试一下,看看BSOptValue(-1, 100,95,8%,3%,,20%)的值是否为【而BSOptValue(1, 100,95,8%,3%,,20%)的值为,与前面的一样】:
Function BSOptValue(iopt, S, X, r, q, tyr, sigma)
' Returns Black-Scholes Value (iopt=1 for call, -1 for put; q=div yld)
' Uses BSDOne fn
' Uses BSDTwo fn
Dim ert, eqt, NDOne, NDTwo
ert = Exp(-r * tyr)
eqt = Exp(-q * tyr)
NDOne = (iopt * BSDOne(S, X, r, q, tyr, sigma))
NDTwo = (iopt * BSDTwo(S, X, r, q, tyr, sigma))
BSOptValue = iopt * (S * eqt * NDOne - X * ert * NDTwo)
End Function
Function BSDOne(S, X, r, q, tyr, sigma)
' Returns Black-Scholes d1 value
BSDOne = (Log(S / X) + (r - q + * sigma ^ 2) * tyr) / (sigma * Sqr(tyr))
End Function
Function BSDTwo(S, X, r, q, tyr, sigma)
' Returns Black-Scholes d2 value
BSDTwo = (Log(S / X) + (r - q - * sigma ^ 2) * tyr) / (sigma * Sqr(tyr))
End Function
注意,中间量DOne和DTwo已经改写成了函数,并在主函数中被调用。这样做的目的是使VBA代码模块化,从而使代码显得更明晰,更容易发现错误所在。
总而言之,函数是一种有返回值的VBA程序,它们的代码封装在关键词Function和End Function中。紧跟关键词Function之后的是函数名称,它后面括号中的是一系列输入变量。接下来的是一组VBA语句,当函数运行时,输出值由函数名返回:
Function name(input1,input2,…)
[statements]
[name = expression]
End Function
在一个好的VBA程序中,每一个模块页都应该以Option Explicit语句开始,它强迫所有的变量都必须声明。注意,函数使用的变量都应该用Dim语句声明,但函数的输入变量不需要声明。如果代码中需要使用Excel函数,则必须在函数名前面加上Application.或者WorksheetFunction.前缀。
在VBA中操作数组
事实上,带有数组输入或输出的函数编码是比较复杂的。因此,在编写这类函数之前,有必要先弄清楚Excel是如何处理数组的。在许多实例中,大家熟悉的Excel函数,如SUM,AVERAGE,STDEV,SUMPRODUCT和NPV,都可以用来处理数组。而在其他情形下,则需要一些额外的安全措施才能使函数按预想的方式执行。特别是:
在没有合适的内嵌函数的情况下,处理数组时,需要在一个循环体中执行重复操作。这样的话,就必须先计算数组元素的个数,使用Excel函数COUNT可以很容易地得到它,如(avec)。
必须清楚数组的编号方式。没有Option Base语句时,则元素从0(缺省值)开始编号。模块页开头有Option Base 1语句时,则数组中的元素从1开始编号,就象Excel通常的操作。
进行矩阵操作,如矩阵相乘时,需要先检查一下矩阵维数的一致性。(一致性是指,如果矩阵相乘,则前一矩阵A的列数必须等于后一矩阵B的行数。)对于任何矩阵操作,都需要一些额外的代码来确保矩阵的格式是正确的(行向量或列向量)。
附录4A通过一些简单的函数演示如何在VBA中处理数组,重点放在操作矩阵方面。
接下来的两节将演示如何处理数组,节中的variance函数直接处理数组中的单个元素,而节中则使用Excel矩阵相乘函数和其他数组函数来间接处理它们。
数组变量的期望和方差函数
假设一项投资的未来现金流是不确定的,有五种可能结果,每结果出现的概率在图中的第一列显示。计算现金流的期望值需要用每一现金流乘以它出现的概率,然后将乘积相加,如D列所示。
Expected Values:期望值
【参照书中第79页的图】
图 VBFNS工作簿ExpValues表中的现金流及其概率
采用Excel函数来计算,可以写成一条简单的公式:
SUMPRODUCT(B5:B9,A5:A9)
注意,SUMPRODUCT函数中的两个数组要么都是列向量(本表),要么都是行向量。另外,如果第二个数组的(这里是A5:A9)数值之和不为1,则公式计算出来的期望值就是错误的。
在工作表中计算方差则显得不那么紧凑,它需要先计算每个现金流与平均值的偏离量,再用偏离量的平方与概率相乘,然后相加,如列E所示。因为在函数中,对每个元素的计算是‘离表(off sheet)’执行的,所以使用VBA函数来计算方差(以及计算现金流的期望值)将更有优势。两个函数,WVariance和ExpVal,都有两个数组形式的输入变量,它们是计算期望值时需要用到的现金流数组vvec和概率数组pvec。
ExpVal(vvec,pvec)函数的代码中主要调用了Excel函数SUM,COUNT和SUMPRODUCT。由于Excel函数为这个函数处理了所有的数组操作,因此它的代码很直接。前面的If…Then条件语句滤除了那些期望值无法有效计算的情况,也就是,如果数组pvec中的数值相加不为1,或两个数组中的元素个数不相时,将函数返回-1:
Function ExpVal(vvec, pvec)
' returns expected value for 2 arrays
If (pvec) <> 1 Or _
(vvec) <> (pvec) Then
ExpVal = -1
Exit Function
ElseIf <> Then vvec = (vvec)
End If
ExpVal = (vvec, pvec)
End Function
要使SUMPRODUCT函数能够正常运行,两个数组形式的输入变量必须是同一类型的,要么都是列向量,要么都是行向量。如果输入变量不是同一类型的,就需要将vvec转置。
WVariance函数的VBA代码中需要处理数组中的单个元素。模块页开头的声明Option Base 1确保了数组元素是以1开始编号的(与缺省值0相反)。与ExpVal函数一样,条件语句滤除了数组pvec中的数值相加不为1,或两个数组中的元素个数不相等的情况:
Option Base 1
Function WVariance(vvec, pvec)
'Returns variance of array of values, with attached probabilities
'Uses Function ExpVal
Dim Expx 'expected value
Dim i As Integer, n As Integer
If (pvec) <> 1 Or _
(vvec) <> (pvec) Then
WVariance = -1
Exit Function
If <> Then vvec = (vvec)
MsgBox ("vvec(1)" & vvec(1))
End If
Expx = ExpVal(vvec, pvec)
WVariance = 0
For i = 1 To (pvec)
WVariance = WVariance + pvec(i) * (vvec(i) - Expx) ^ 2
' MsgBox ("WVariance " & WVariance)
Next i
WVariance = (WVariance)
End Function
同样,If…Then条件语句滤除了那些导致方差无法有效计算的情况。方差用一个For…Next循环来求得。注意,偏离值的平方表示成:(vvec(i) - Expx) ^ 2的形式,这里,expx是现金流vvec(i)的期望值。这个值是用我们刚刚建立的VBA函数ExpVal求得的,因此VBA代码为:
Expx = ExpVal(vvec, pvec)
在循环指针i每循环一次加1的同时,方差也随之增加pvec(i) * (vvec(i) - Expx) ^ 2。
作为练习,可以编写一个函数来计算现金流的标准差。记住,要使用VBA的内嵌函数Sqr,而不能用Excel函数。在求平方根之前,先检查一下WVariance函数求出的值是否有意义(也就是说,它不能为负),这是一种很明智的做法。(作为练习4的答案,求标准差的VBA代码放在本章的尾部。)
当创建的函数不能工作时,可能需要在适当的位置增加一些MsgBox函数来显示中间变量的值。例如,将图中单元格A5的概率值由改成,如果在语句Exit Function之前插入一条信息(MsgBox “Got here”),就可以检测函数的处理流程。
测试程序的另一种方法是在一个子程序中调用它。这时会出现运行时错误,也可以使用调试器来跟踪代码。例如,编写一个CheckFn宏来建立数组vvec和pvec,然后调用函数WVariance。如果代码中有错误,调试器会弹出错误信息框:
Sub CheckFn()
‘Calls WVariance function
‘needs named ranges vvec,pvec in spreadsheet
Dim vvec As Variant,pvec As Variant
Set vvec = Range(“vvec”)
Set pvec = Range(“pvec”)
Range(“B22”).Value = WVariance(vvec,pvec)
End Sub
有必要为函数的用途写一段简要的描述,这样在函数列表中访问该函数时,它就会出现对该函数的描述。在VBE中,保持相应的模块页在激活状态。然后访问对象浏览器,观察Excel中不同对象的方法和属性。在库中选择当前的工作簿,并选择函数代码所在的模块,然后在成员窗口选择你创建的函数。点击鼠标右键,选择选项属性,这时会出现一个对话框,这里可以为你创建的函数增加一段简单的描述。最后,点击确定按钮来关闭对话框。可以通过粘贴函数按钮来访问该函数,检查刚才添加的描述信息是否出现。
数组变量的组合方差函数
计算投资组合收益的均值和方差的函数是很容易编写的。为了演示这一过程,我们用Excel函数写一个工作表公式,然后将公式用VBA代码完成,并产生出相应的函数。
图中的工作表包含有三种资产的风险和收益信息,分别是A、B和C,并显示了等权重时投资组合的计算结果。资产的收益信息在单元格C5:C7中,为列向量,在工作表公式中可以用区域名称e来引用,而在VBA代码中用retsvec来引用。组合由三种资产组成,权重放在单元格区域E5:E7中,也是列向量,在工作表公式中用区域名称w引用,在VBA代码中用wtsvec来引用。
Portfolio Risk&Return:资产组合的风险和收益;
Asset Data:资产数据;Correlations:相关系数;VCV Matrix V:VCV矩阵V
【参照书中第82页的图】
图 投资组合中三种资产的风险和收益
组合的期望收益(单元格G5中)等于每种资产的期望收益乘以它们的权重然后相加(实际上等于或者%)。G5中的公式SUMPRODUCT(e,w)给出了加权收益,它使用了区域名称w和e,这两个列向量的元素个数是相同的。
三种资产的方差——协方差矩阵可以用相关系数矩阵(C10:E12)以及单个资产的标准差(D5:D7)计算得到。其结果(C15:E17)矩阵的对角线上是单个资产的方差,而其他则是两种不同资产的协方差。假设此方差——协方差矩阵的区域名称为V。方差——协方差矩阵,再加上权重数组,构成了计算投资组合方差的基础,计算时采用矩阵相乘的形式,其中表示权重向量w的转置。在单元格G17的计算方差公式中,调用了数组转置Excel函数TRANSPOSE和矩阵相乘函数MMULT。公式如下:
MMULT(TRANSPOSE(w),MMULT(V,w))
(由于这是一个数组函数,因此在将公式输入到G17中之后,别忘了按下组合键Ctrl+Shift+Enter。)
要使Excel函数MMULT能够正常工作,相乘矩阵的维数必须相符,即,前一个矩阵的列数必须等于后一个矩阵的行数。这里TRANSPOSE(w)是一个1×3的行向量,而MMULT(V,w)是一个3×1的列向量,因此矩阵相乘是可以的,其结果为(或者%)。因此,对这个资产组合来说,收益的风险为。
编写计算资产组合收益的函数其实很简单,我们将这个函数取名为PFReturn(wtsvec,retvec):
Option Base 1
Function PFReturn(retsvec, wtsvec)
'calculates weighted return for PortFolio
If (retsvec) <> (wtsvec) Then
PFReturn = "Counts not equal"
Exit Function
ElseIf <> Then
wtsvec = (wtsvec)
End If
PFReturn = (retsvec, wtsvec)
End Function
第二个函数PFVariance(wtsvec,vcvmat)的代码则有点复杂:
Function PFVariance(wtsvec, vcvmat) 'calculates PF variance
Dim nc As Integer, nr As Integer
Dim v1 As Variant
nc =
nr =
If nc > nr Then wtsvec = (wtsvec)
v1 = (vcvmat, wtsvec)
PFVariance = (v1, wtsvec)
End Function
这个函数既有向量形式的输入变量,又有矩阵形式的输入变量,这就要求VBA代码先要检测矩阵相乘的一致性。我们希望不管权重数组是行向量还是列向量,函数都能正常工作。这就需要考虑数组的维数了。与权重数组wtsvec有关的变量nr和nc(分别代表数组的行数和列数)被声明为整数型。为了使矩阵相乘能够正常执行,权重向量必须是一个列向量,因此有语句:
If nc > nr Then wtsvec = (wtsvec)
在矩阵相乘时使用了一个临时变量v1,它在前面声明为Variant型变量。节曾经提到过,这是一个非常灵活的数据类型,可以存储任何形式的数据,标量、数组或矩阵。在这里v1存储的是一个3×1的列向量Vw。在VBA中一个能很方便地计算的方法是使用SUMPRODUCT函数,它将两个3×1列向量w和Vw中的元素分别相乘再相加。因此倒数第二条语句:
PFVariance = (v1, wtsvec)
返回一个标量给PFVariance。
PFVariance函数代码的重点在于确保Excel函数MMULT能够正常计算。由于函数的参数是用户输入的,因此必须编写代码来调整这些输入,以确保矩阵相乘的一致性。但是,由于MMULT函数和SUMPRODUCT函数处理的都是数组计算,因此没有必要在代码中为数组v1定义维数,或操作单个元素。这使得代码显得相对简单一些。
虽然工作表演示的是一个由三种资产组成的组合,但这两个函数实际上可以处理任何大小的数组。
输出数组形式的函数
前面提到的所有函数都是将返回值传递给单个单元格(也就是标量输出)。有时,我们希望函数能返回一组数据,就像LINEST函数那样,它为一组有k个解释变量的数据集返回一个5×(k+1)的回归结果数组。一般来说,编写带有数组输出的函数是有困难的,因为它需要正确地配置和构建数组。另外,用户还需要线索来为公式的输出选择适当大小的区域。函数名称可以用来暗示函数返回的是向量还是矩阵,并且,在函数列表的函数描述栏可以提供输出结果的维数。在代码中,变量名和定义的变量类型可以用来暗示是否需要对单个元素进行处理。
这种方法可以用一个计算汇总统计量的例子来演示:显示一个数据集的百分比分布。其目的是用一个函数返回向量或矩阵形式的计算结果。
假设数据集由许多价格数据组成,命名为dvec,如图第一列所示。注意,图中只显示了120个数据中的12个数据。
如果低于某一价格的元素个数占总数的10%,20%,30%...,则这个价格被称为数据集分布的十分位数。它们只是百分位累积分布的一个特例。就像数据集dvec的均值可以用一个Excel函数计算一样,我们也可以用PERCENTILE函数计算出不同的百分位数。加上0%的十分位数(即最小价格),总共有11个十分位数值。图显示了这些十分位数以及它们对应的百分比。在作图时,这个11×2的数组就可以用来显示数据的累积分布情况了。现在,我们的目的是编写一个函数来返回一个11×2的数值矩阵。
Data for Summary:需要汇总的数据
【参照书中第84页的图】
图 DecilesMat函数计算出的统计值
函数DecilesMat(dvec)的代码如下。由于我们已经知道数组的维数,因此在定义数组时,可以直接给出它的维数(Dim decmat(11,2))。第一列存放用Excel函数PERCENTILE计算出的十分位数值;而第二列存放对应的百分比,0%、10%、20%等。
Option Base 1
Function DecilesMat(dvec)
' returns 11 by 2 array of deciles ready for X-Y plot
Dim i As Integer
Dim decmat(11, 2) As Variant 'output array
For i = 0 To 10
decmat(i + 1, 1) = (dvec, * i)
decmat(i + 1, 2) = * i
Next i
DecilesMat = decmat
End Function
使用该函数时,先选中一个11×2的单元格区域,然后键入数组公式,或者通过函数列表来输入函数以及所需的数据集,最后按下组合键Ctrl+Shift+Enter。
在用户定义函数中调用Excel和VBA函数
我们已经看到,可以在VBA代码中调用标准的Excel函数,如COUNT和SUMPRODUCT等,只需在函数名前加上前缀Application.或WorksheetFunction.即可。目前,我们还可以通过Excel Visual Basic帮助来得到‘可用于Visual Basic的Worksheet函数列表’。但是在最近的Excel升级版本中,调用Excel函数被证明是不牢靠的。还有一点,尽管大部分Excel函数可以在VBA代码中被调用,但并不是所有的标准函数都可以使用。不过,调用Excel函数不仅可以巩固用户的Excel知识,也可以减少编写函数的工作量。
另外,VBA也提供了一系列的标准函数,它们的用法将在下面的章节时作简单介绍。
在用户定义函数中使用VBA函数
使用VBA函数时不需要任何前缀,如MsgBox、InputBox等等。有少量的Excel函数可以用标准VBA函数代替,如Excel函数SQRT可以用VBA函数Sqr代替。并且要牢记,在编写函数时,必须用VBA函数而不要用功能相同的Excel函数。
VBA也有一些数学函数:Abs、Atn,Cos,Exp,Log,Randomize,Rnd,Sgn,Sin,Sqr和Tan。在VB编辑器的对象浏览器的Math模块中可以找到这些函数。除了Randomize,其他的数学函数都有相应Excel函数,但对应的Excel函数的名称是不同的,它们分别是ATAN、LN、RAND、SIGN和SQRT。
VBA中还有一些‘转换’函数:其中比较有用的有Fix、Hex、Int和Oct。用小写字母输入函数名时,如果VBA认识这个函数,它会自动地把函数名变成大写。
总之,在Excel函数和VBA函数都可用的情况下,必须优先选择VBA函数。它们在使用时不需要加Application.前缀。
加载宏
分析工具库(ATP)中包含了一些附加的函数,如COUPDAYS、LCM和MULTINOMIAL等,它是Excel的加载宏。如果工具菜单中没有数据分析选项(见节),则必须先安装分析工具库。安装之后,就可以在函数列表中看到大写的ATP函数名了。
要想在VBA模块页中使用ATP函数,必须先做两件事:
在Excel中,用工具菜单中的加载宏来安装分析工具库-VBA函数。VBA等价函数会以复件的形式出现在函数列表中,尽管名称是大小字母混合的。
在VBE中,调用工具菜单中的引用选项来连接文件。(这个文件在步骤1中被加载。)现在,在VBA代码中,可以象使用标准VBA函数一样使用ATP函数(也就是不需要Application.前缀)。没有这个引用,VBA就不能认出这些函数。
编写VBA函数的优缺点
关于编写VBA函数来进行工作表计算的优缺点,应该注意以下几点。
优点:
相对于工作表计算和复杂的单元格公式,函数的优点在于可以将大量的计算压缩到一个单元格中。如果编码正确并且函数名容易理解,用户定义的函数可以为单个单元格带来相当强大的功能。在单元格中输入函数比输入复杂公式出错的可能性要小。如果函数名和参数名都很清晰易懂,那么对于半熟练的用户来说,使用它们比使用复杂的公式更容易一些。要想增强用户对使用函数的信心,最重要的一点是,必须对VBA代码作清晰的注释。
函数还有方便移植的优点,所有能访问到它们的VBA代码的工作簿中,都可以使用它们。将工作簿A中的函数移植到工作簿B中的最佳办法是:将A模块(如ModuleA1)中的代码拷贝到B的模块(如ModuleB1)中。注意,只需在ModuleA1中用常用的拷贝粘贴方法将代码复制到ModuleB1中即可。不需要从工程资源管理器窗口中‘导出’模块页。为了方便移植,最好将相关的函数放在同一个模块页中,例如,VBFNS工作簿的ModuleB1页中包含了所有与布莱克-舒尔斯有关的函数。
函数也可以扩展(例如,为三种资产编写的函数可以扩展到处理任何数量的资产)。为了更加灵活方面,有些函数也可以合并起来(例如,在BSOptValue函数中增加一个iopt参数就可以同时计算看涨期权和看跌期权的价格)。
有时,函数的存在意味着不必在工作表中建立复杂的结构。例如,如果将二叉树的层次作为函数的一个参数,就没有必要为不同的层次复制二叉树结构。(见附录4B用二叉树方法为期权定价的函数。)
缺点:
有些人认为使用VBA编码语言是用户定义函数的一大缺点。但需要强调的是,编写函数只需要熟悉VBA的一个小子集。编程所需的句法和控制语句本质上是程序设计的基础。这是传统的编程语言而不是VBA语言面向对象部分的内容。
函数不可能完成的任务
有些操作不能写成函数。在VBA中重写程序行代码,或者插入临时的空工作表,或者使用规划求解来求解最优化问题,这些都不能写成函数的形式。同样,有些操作,如建立图表,或根据某些值改变单元格的颜色等,都不易用函数来实现。
本章附录4A用Excel矩阵函数演示了操作数组的一些方法。附录4B介绍用二叉树方法为期权定价的函数。本章的最后是一些编写函数的简单练习,及简要答案。
小结
编写VBA函数要利用用户的工作表知识以及Excel函数知识。也涉及到程序设计方面的知识,但只是在一个直接并显而易见的水平上利用。
相比操作对象的子程序而言,VBA函数能够返回数据,函数中的任何计算都以‘离表(off-sheet)’的方式执行。函数与子程序的一个关键区别在于,函数是‘被动的(passive)’,也就是说,函数中的代码不能操纵单元格区域。
许多Excel内嵌函数都可以在用户定义函数中调用:在VBA代码中,它们名称前需要加上前缀Application.或WorksheetFunction.。VBA还提供一些自己的函数,这些函数在调用时不需要加前缀,并且可以在对象浏览器中引用。
用户定义函数带有参数,它们可以是数值、单元格输入或数组输入。函数参数在Function语句中被隐式声明,因此不需要再显式声明。而函数中使用到的所有其他变量都应该预先声明,并赋予适当的数据类型。
函数可以返回单个数据,也可以返回一组数据。通常,处理数组输入和数组输出的函数会更难于编写。
在工作表中,可以直接键入函数及参数,也可以用粘贴函数按钮来调用用户定义函数。它们在函数列表的用户定义类别中列出。可以写一些简单的文档来描述函数的功能以及所需的参数,这样,当在函数列表对话框中访问函数时,就会出现一些有用的帮助信息。
对于复杂的函数,采用模块化的编程方法是很明智的,先建立一系列中间函数,然后一个个测试。
附录4A 演示函数如何处理数组
第一个例子,函数ArrayDemo1,演示Excel矩阵相乘及矩阵求逆的使用方法。要使函数正常运行,Amat1和Amat2都必须是方阵,并且具有相同的维数。由于所有的矩阵操作都是由Excel函数完成的,所以代码很直接。Amat12是两个矩阵的乘积,它也是一个矩阵,在开始时它被定义成Variant型,说明它的类型和维数都不确定:
Function ArrayDemo1(Amat1, Amat2)
' Uses the MMult and MInverse Array functions
Dim Amat12 As Variant
Amat12 = (Amat1, Amat2)
ArrayDemo1 = (Amat12)
End Function
如果一个矩阵与向量相乘,就必须确保两者的维数一致。如果Amat1是一个3×3的矩阵,而avec是一个3×1的列向量,则相乘的结果是一个3×1的列向量。函数ArrayDemo2的输入是avec和Amat1,输出结果是一个列向量:
Function ArrayDemo2(avec, Amat1)
'post multiplying matrix (Amat1) by col vector (avec)
ArrayDemo2 = (Amat1, avec)
End Function
如果向量转置成行向量,它可以‘左乘以’矩阵Amat1,但结果将是不同的,实际上,结果是一个1×3的行向量。第三行应该改成:
ArrayDemo2 = ((avec),Amat1)
通常,我们将函数写成像Excel函数那样的表现形式。也就是说,不管输入的数组是行向量还是列向量,我们的函数能够处理。而函数的输出向量必须是一个行向量。函数ArrayDemo3返回一个行向量,而不管输入的数组avec是什么形式(也就是不管avec是行向量还是列向量)。如果avec是一个列向量,则它的行数将超过它的列数,在这种条件下,函数会将这个向量转置成一个行向量。而如果avec是一个行向量,则不用处理。函数ArrayDemo3返回一个行向量,不管输入的向量是何种形式:
Function ArrayDemo3(avec)
' returning a row vector, whatever the input
Dim nr As Integer, nc As Integer
nr =
nc =
If nr >= nc Then avec = (avec)
ArrayDemo3 = avec
End Function
下一个函数,ArrayDemo4,演示怎样用VBA代码逐元素(element-by-element)地建立数组。这里,要着重强调的是,构建的这些数组在缺省情况下都是行向量。基于这个原因,矩阵相乘要按下面代码中的方式执行,并返回一个行向量:
Function ArrayDemo4(Amat1)
' VBA sets up array avec as row array
Dim avec(3) As Variant
avec(1) = 3
avec(2) = 4
avec(3) = 5
ArrayDemo4 = (avec, Amat1)
End Function
另外,如果倒数第二行的代码改为:
ArrayDemo4 = (,Amat1,(avec))
则会执行一个不同的矩阵相乘运算,并返回一个不同值的列向量。
模块页开头的Option Base 1语句确保数组元素从1开始编号,就像Excel平常操作的那样。如果Option Base语句被忽略,则数组元素从0开始编号(缺省情况)(那么接下来的矩阵相乘会由于维数不一致而失败)。这里,Option Base语句确保数组avec中的元素只有三个:avec(1)、avec(2)和avec(3),而没有元素avec(0)。
当Excel中没有合适的数组函数来满足数组操作的需要时,那么,在一个循环体内对数组中的每一个元素进行处理就变得不可避免了。这样,必须先计算出数组的元素个数,我们使用(avec) 。(对于用户定义函数自身创建的数组,元素个数可以用数组的UBound(和LBound)计算得到。)
附录4B 二叉树期权定价函数
布莱克-舒尔斯公式给出了欧式看涨期权和看跌期权的价格。另外一种期权定价的替代方法是二叉树方法,它可以用于更广泛的期权定价。这种方法在期权生存期内使用一系列概率驱动的二叉分支(股票价格只有以确定的数量向上和向下运动两种方式)来近似股票价格的变动。这个二叉树结构产生了一个股票价格树,价格树有一组的终端价格和一簇价格运动路径。对于树的每一个节点,股票期权的价格都可以计算出来,并且使用树的概率结构,期权的收益可以通过折现来得到它的现值。
CRR Option Valuation:CRR期权定价
【参照书中第90页的图】
图 用CRR二叉树方法计算期权价格
图中的工作表显示了计算看涨期权价格的细节(在单元格G17中使用布莱克-舒尔斯函数BSOptValue),以及一个九期二叉树的组成部分。期权的生存期(T = 年)被分成九个期间,每个期间的长度为(这里是年)。现在的目的是编写另一个期权定价函数,是基于二叉树定价方法的期权定价函数。要理解VBA代码,首先应该理解二叉树是如何定价的,因此现在需要暂时脱离主题,来了解一下这种方法的工作原理。
每一期计算都需要用到股票价格向上和向下运动的乘数(u和d)以及相应的概率p和,称为考克斯,罗斯和鲁宾斯坦(CRR)参数。参数的计算公式能够保证股票的价格树与一个几何扩散过程一致,该过程可以认为是股票价格运动的真实过程。关于u、d、p和的公式这里不必考虑,这些公式用于计算起始价格以及所有九期价格时,会生成图所示的股票价格树。
Binomial Tree Valuation:二叉树定价
Share:股票
【参照书中第90页的图】
图 使用CRR参数计算的九期股票价格树
从价格100开始,第一期有两种可能的价格,分别是(100u)和(100d)。经过九期运动之后,一支向上运动了i次(向下运动了(9-i)次)的股票价格为,或者对于n级树:
这使得到期日的期权有10种收益,如图中的单元格区域K49:K58所示,它们可以用下面的公式计算得到:
for i=0,1,…,9 and S=100,X=95
例如,对于就此向上运动的股票,期权价格为:
最后一步操作是对向量单元格K49:K58中的数据定价,将它们分别乘以它们发生的概率,然后折现到0时刻。这步操作需要对终期期权收益作‘倒推’处理,使用向上运动和向下运动的概率,p和p*,并折现得到第8期中的数据,依此类推,直到得到它们的当前值。例如,在第8级,单元格J50中的期权价格为。每一期的计算结果都显示在图中,因此通过二叉树定价方法计算出的最终期权价格显示在单元格B58中(结果为,而布莱克-舒尔斯公式的计算结果为)。
Option Value:期权价值
【参照书中第91页的图】
图 倒推并折现期权价格
如下面显示,BinOptVal函数的VBA代码以CRR树为基础。严格地说,模块页开头的Option Basic 0语句是不必要的。它只是用来强调在这个函数中,所有的数组元素都是从0开始编号的。函数有八个参数,包括nstep,这个参数代表二叉树的级数。函数返回一份欧式期权的价格(iopt=1时计算看涨期权的价格,iopt=-1时计算看跌期权的价格)。在本节的最后我们会明白,很容易对这个函数进行扩展,使它能够同时计算美式期权的价格:
Option Base 0
Function BinOptVal(iopt, iea, S, X, r, q, tyr, sigma, nstep)
' Returns Binomial Option Value (iopt = 1 for call, -1 for put;
' iea=1 for euro, 2 for amer)
Dim delt, erdt, ermqdt, u, d, P, pstar
Dim i As Integer, j As Integer
Dim vvec As Variant ' to be a vector
ReDim vvec(nstep) ' known size of vector
' calculate parameters
delt = tyr / nstep ' length of time step
erdt = Exp(r * delt) ' compounding factor
ermqdt = Exp((r - q) * delt) ' dividend effect
u = Exp(sigma * Sqr(delt)) ' up multiplier
d = 1 / u ' down multiplier
P = (ermqdt - d) / (u - d) ' up prob
pstar = 1 - P ' down prob
'calculating vector of option values after 9 steps
For i = 0 To nstep
vvec(i) = (iopt * (S * (u ^ i) * (d ^ (nstep - i)) - X), 0)
Next i
'calculating conditional payoffs & discounting back step by step
For j = nstep - 1 To 0 Step -1
For i = 0 To j
vvec(i) = (P * vvec(i + 1) + pstar * vvec(i)) / erdt
Next i
Next j
BinOptVal = vvec(0)
End Function
定价过程的重点在于向量数组vvec(),它被定义成Variant类型。在nstep=9时,vvec()有10个值,vvec(0)到vvec(9),并使用语句ReDim vvec(9)定义维数,要想该语句在各级通用,可以写成vvec(nstep)。
定义完变量之后,开始对CRR二叉树向上和向下运动的参数赋值。对于图中所示的看涨期权,vvec()的初始值为:vvec(0)=,vvec(1)=,…,vvec(9)=0,它们其实就是图的K49:K58中显示的期权收益。在VBA代码中,这些值用下面的公式计算得到:
生成vvec()数组初始值的语句为:
For i = 0 To nstep
vvec(i) = (iopt * (S * (u ^ i) * (d ^ (nstep - i)) - X), 0)
Next i
接下来的五行代码沿着树形结构向后倒推、计算价格并折现:
For j = nstep - 1 To 0 Step -1
For i = 0 To j
vvec(i) = (P * vvec(i + 1) + pstar * vvec(i)) / erdt
Next i
Next j
检查代码,在每一个倒推步中(j的值),vvec()数组中的值被重新计算,并用计算出来的新价格取代原来的期权价格。这样做有一个好处,只需要能够存储一个(n+1)向量的有限存储空间即可。这样在九期二叉树中,当j=8时,vvec()被重新计算,结果变为:vvec(0)=,vvec(1)=等等,而在最后,当j=0时,vvec(0)变为。(作为对比,在工作表中,从第9期向第0期倒推,需要我们显示10个向量的值,如图所示)。因此,当j=0时,期权的即期价格就放在vvec(0)中。
如单元格G15所示,使用这个函数用九期二叉树来计算看涨期权的价格,我们有:
BinOptVal(1,100,95,8%,3%,,20%,9)
其返回值为。一般来说,当分的期数越多时,计算出的二叉树期权价格与布莱克-舒尔斯价格会越接近。例如,BinOptVal(1,100,95,8%,3%,,20%,50)的返回值是。要验证这一点,可以先构建一个数据表,然后使用不同的期数来计算结果。
图,,中显示的是一个看涨期权的二叉树定价情况。如果期权是一个看跌期权,则需要将输入参数iopt改为iopt=-1,并且将生成vvec()数组初始值的语句变为:
For i = 0 To nstep
vvec(i) = (-1 * (S * (u ^ i) * (d ^ (nstep - i)) - X), 0)
Next i
它给出了看跌期权的终期收益。验证一个看跌期权的计算结果:
= BinOptVal(-1,100,95,8%,3%,,20%,9)
其结果为,而布莱克-舒尔斯价格为。
美式期权与欧式期权唯一的区别在于它可以提前执行,也就是说,它可以在期权到期之前执行。但是对美式看涨期权来说,提前执行期权永远是不值的(在没有股利的情况下),因此它们的定价与欧式期权完全一样。但是,对于美式看跌期权来说,提前执行有时比持有期权更具价值。为了在定价函数BinOptVal中调整VBA代码,我们可以增加一个输入参数(iea=1表示计算欧式期权,iea=2表示计算美式期权),然后增加一条代码:
'for Amer options, allowing for early exercise at each step
If iea = 2 Then vvec(i) = (vvec(i), iopt * (S * (u ^ i) * (d ^ (j - i)) - X))
则合并后的整个二叉树定价函数代码如下,它可以用来计算欧式期权以及美式期权的价格:
Function BinOptVal(iopt, iea, S, X, r, q, tyr, sigma, nstep)
' Returns Binomial Option Value (iopt = 1 for call, -1 for put;
' iea=1 for euro, 2 for amer)
Dim delt, erdt, ermqdt, u, d, P, pstar
Dim i As Integer, j As Integer
Dim vvec As Variant ' to be a vector
ReDim vvec(nstep) ' known size of vector
' calculate parameters
delt = tyr / nstep ' length of time step
erdt = Exp(r * delt) ' compounding factor
ermqdt = Exp((r - q) * delt) ' dividend effect
u = Exp(sigma * Sqr(delt)) ' up multiplier
d = 1 / u ' down multiplier
P = (ermqdt - d) / (u - d) ' up prob
pstar = 1 - P ' down prob
'calculating vector of option values after 9 steps
For i = 0 To nstep
vvec(i) = (iopt * (S * (u ^ i) * (d ^ (nstep - i)) - X), 0)
Next i
'calculating conditional payoffs & discounting back step by step
For j = nstep - 1 To 0 Step -1
For i = 0 To j
vvec(i) = (P * vvec(i + 1) + pstar * vvec(i)) / erdt
'for Amer options, allowing for early exercise at each step
If iea = 2 Then vvec(i) = (vvec(i), iopt * (S * (u ^ i) * (d ^ (j - i)) - X))
Next i
Next j
BinOptVal = vvec(0)
End Function
前面计算出来的欧式看跌期权价格为,而采用九期二叉树计算出来的美式看跌期权的价格为。
编写函数练习
1. 金融算术函数
编写如下简单金融计算函数:
FutrueValue(P,r,n,t),该函数返回投资P在t年后的未来价值,假设收益年利率为r,一年计算n次复利。
AnnPercentRate(rf,f)函数,该函数返回年利率为rf,一年计算f次复利的实际利率。
APRCont(r)函数,该函数返回连续复利为r的实际年利率。
2. 和与积和函数
编写一个求和函数,该函数的功能与Excel函数SUM相同,用来将一个数组中的数值加总。
编写一个求积和函数,模仿Excel函数SUMPRODUCT(array1,array2),它用来计算两个数组的元素乘积之和。在两个数组大小不一致时,返回错误信息。
3. 净现值函数
编写一个函数NetPV(cvec,rate),功能与Excel函数NPV相同,该函数需要两个参数,第一个参数是现金流数组,第二个参数是折现率。
4. 现金流标准差函数
编写一个函数,用来计算不同权重(pvec)的一组现金流(vvec)的标准差,如节中讨论的那样。记住,要使用VBA的内嵌函数Sqr,而不能使用Excel函数。
回收期函数
工作表中显示了一项投资计划在未来五年的现金流。现金流初值是负的,而在第三年里,累积现金流变为正号,这个时刻被称为工程的回收期(在这里是年)。
决定回收期的中间计算已显示在表中。其重点在于计算每年的累积现金流,以及判断累积现金流首次变为正数的时刻。编写一个回收期函数PayBack(cvec),它返回任何现金流数组的回收期。
【参照书中第95页的图】
练习答案
金融算术函数代码
Function FutureValue(P, r, n, t)
'returns future value of P, compounded at annual rate r,
'n times a year, for t years
FutureValue = P * (1 + r / n) ^ (n * t)
End Function
Function AnnPercentRate(rf, f)
'returns annual effective rate of compounding at rate rf, f times .
Dim APR
APR = (1 + rf) ^ f - 1
AnnPercentRate = APR
End Function
Function APRCont(r)
'returns annual effective rate of compounding continuously at rate r .
APRCont = Exp(r) - 1
End Function
2. 和与积和函数代码
数组求和:
Option Base 1
Function SumArray(avec)
'VBA version of Excel's SUM function
Dim n As Integer: Dim i As Integer
Dim total
n = (avec) 'counts all elements in array
total = 0
For i = 1 To n
total = total + avec(i)
Next i
SumArray = total
End Function
数组求积和:
Option Base 1
Function ProdArray(xvec, yvec)
'VBA version of Excel's SUMPRODUCT function
Dim i As Integer, j As Integer
Dim imax As Integer, jmax As Integer
imax = (xvec) 'counts all elements in 1st array
jmax = (yvec) 'counts all elements in 2nd array
If imax = jmax Then
ProdArray = 0
For i = 1 To imax
If (xvec(i)) And (yvec(i)) Then _
ProdArray = ProdArray + xvec(i) * yvec(i)
Next i
Else: ProdArray = "Counts don't match"
End If
ProdArray = ProdArray
End Function
3. 净现值函数代码
Option Base 1
Function NetPV(cflowvec, rate)
Dim i As Integer
NetPV = 0
For i = 1 To (cflowvec)
NetPV = NetPV + cflowvec(i) / (1 + rate) ^ (i - 1)
Next i
NetPV = NetPV
End Function
4. 现金流标准差函数代码
Option Base 1
Function StdDev(vvec, pvec)
Dim Var
If (pvec) <> 1 Or _
(vvec) <> (pvec) Then
StdDev = -1
Exit Function
'ElseIf <> Then vvec = (vvec)
End If
Var = WVariance(vvec, pvec)
StdDev = Sqr(Var) 'Sqr(arg1) is VBA Math Fn
StdDev = (StdDev, 2) 'to 2
End Function
回收期函数代码
函数代码如下,注意用粗体显示的三个特点。
第一,数组cvec中的元素要求必须是数值。这里,Option Base 1语句确保数组cvec中的元素从1开始编号,cvec中的元素可以用cvec(1)、cvec(2)引用,最后一个元素cvec(6)的值为60。数组中没有元素cvec(0)。
第二,在函数开头,用If…Then…Else语句滤除了那些导致回收期无法计算的情况,这使得函数更加稳健(Robust)。(例如,如果第一个现金流为正,cvec(1)>=0,或者整个现金流的累积值为负,(cvec)<0,则函数返回-1)。
第三点,数组cvec中的元素是逐个处理的,每个现金流被一一加总来得到累积现金流,并用变量csum表示。这种操作在一个For…Next循环体中进行,在循环体中,随着i的不断增加直到最大值((cvec)),单个现金流也被一一加总。当变量csum变为正的时候,程序跳出For…Next循环体(Exit For)并计算回收期。
需要关注一下到底是哪一个现金流导致累积现金流由负变正的。在这个例子中,当i=4时,csum从-20变为+35,csum的最后一个负值-20出现在第2年底。因此,确切的回收期就是2年加上分数20/55,也就是年。
回收期函数的VBA代码:
Option Base 1
Function PayBack(cvec)
' calculates payback period (no. of years to 1 )
' initial cashflow must be negative
Dim csum
Dim i As Integer
If cvec(1) >= 0 Or (cvec) < 0 Then
PayBack = -1
Else
csum = 0
For i = 1 To (cvec)
csum = csum + cvec(i)
If csum > 0 Then
Exit For
End If
Next i
csum = csum - cvec(i)
PayBack = (i - 2 - csum / cvec(i), 1)
End If
End Function
第5章 股票的有关简介
本书的第二部分主要介绍股票方面的相关内容。以下三章将分别介绍几个不同的主题:第6章介绍投资组合的最优化,第7章主要涉及资产定价,第8章则主要研究如何进行业绩评价。本章作为第二部分的简介,扼要地总结了在本书的股票部分所涉及的相关金融理论,以及在电子表格中用到的定量数学方法。
在二十世纪五十年代,哈里·马可维茨创立了投资组合的均值-方差理论。从此以后,金融学开始从经济学中分离出来,成为一门新的学科。但它与经济学仍有很大的联系。马可维茨指出,单只股票的自身风险与它对投资组合整体风险的贡献是有很大区别的。投资组合均值—方差理论的一个重要特点是,它认为收益和风险的关系与均值和方差的关系是一致的。在收益服从正态分布的假设条件下,金融理论可以用许多经典的统计分析方法来研究。随后,在二十世纪六十年代创立了资本资产定价模型CAPM。它是股票期望收益的单因素模型,其中引入的β值是衡量投资组合风险的重要指标。在CAPM模型的基础上,又提出了许多评价投资组合业绩的方法。此后,对CAPM模型的研究主要集中在资产收益的多因子模型上,其中最新的成果就是夏普于九十年代初提出的风格分析(Style Analysis)。下面三章将要介绍的内容包括:风险和收益的表示方法,投资组合的风险和收益,有效边界理论,单因素模型,CAPM,传统的资产业绩评价方法,风格分析,以及风险值VaR。
本书的第二部分,首先按年代顺序介绍了股票投资组合管理方法的发展状况,然后以三个相关的工作簿(,和)为基础,演示了不同金融领域中反复出现的一些重要思想。投资组合理论经常由于计算能力的不足而受到制约。到了今天,在Excel和高性能计算机的帮助下,这方面的制约已经基本不存在了(尽管有些应用只涉及到少量的资产,而不是上千种股票)。
以下三章中用到的最主要计算方法是一种最优化方法,即‘二次规划’。这种方法可以用Excel的规划求解插件来实现。本文将介绍如何构建各种不同的投资组合最优化问题,并正确地使用规划求解来得到每种资产在整个组合中的最优权重。为了方便在第6章推导有效边界和在第8章进行风格分析,本文将对二次规划的相关计算原理和应用进行反复说明。在股票部分的研究中,经常用到的第二种方法是回归方法。第7章将介绍如何用Excel中的回归功能来估计CAPM模型中的β系数。一般来讲,用来估计资产风险和收益的许多计算,都可以用Excel的统计计算功能来实现。第6章和第8章中还介绍了如何应用VBA宏来自动调用规划求解,从而更方便有效地生成各种图表。
三个工作簿(,和)中均包括需要用二次规划来解决的最优化问题,它们是:投资组合风险(边界组合),残差平方和(β估计值)和误差方差(风格分析)。虽然有些简单的问题可能有解析解,但作为一个基本方法,最优化问题通常是通过一个反复迭代的寻找过程来找到最优解。在Excel中,这个寻找过程是由规划求解插件来实现的。规划求解的使用方法将在中的EF2表和的Style表中进行详细介绍。
本部分的股票建模技术不仅对股票本身具有重要意义,而且也为本书的第三部分(股票期权估价)作了铺垫。
第6章 投资组合最优化
本章研究个体投资者如何解决投资组合的最优化问题。所需的背景知识和许多实例可参考Bodie et al的著作(1996,第六章和第七章)。这里,将集中讨论如何利用电子表格来进行相关计算,并进一步研究常见的组合问题类型。在确定各类风险资产的权重时,主要使用了Excel里的规划求解,而对于常见的组合问题,还需要分析资金应如何分配在无风险资产和风险资产上,此时更倾向于使用根据电子表格相关公式设计出来的用户定义函数。第和节主要介绍有关风险资产组合的预备知识。第到节集中介绍如何确定投资组合中各类资产的最优权重。第节介绍风险厌恶的概念以及如何权衡风险与收益。接下来的第节到节,主要介绍如何使用电子表格来解决常见的组合问题。在本章的练习部分还详细介绍了为分析投资组合而设计的用户定义函数和宏,见。
组合的均值和方差
所有的金融教科书都对投资组合的均值—方差模型进行过详尽的描述和解释(如Bodie et al,1996,第七章介绍了风险投资组合的最优化问题)。马可维茨的工作大大简化了投资组合的最优化问题。他指出,在最优化过程中,应该先找到均值-方差的有效投资组合,这些组合就是那些在给定风险水平的情况下期望收益最高的有效点。这里,风险用投资组合的方差来度量(更严格地讲,应该是方差平方根,即投资组合收益的标准差)。在对一组资产建立组合分析模型时,应主要考虑以下两点:
投资组合收益的均值和方差;
用风险-收益空间上的坐标点来表示的投资组合;
在本文的研究中,自始至终都假设投资组合处于完全投资状态,即对权重加上约束条件——所有资产的投资权重之和必须为100%。
给定一组风险资产及其相应权重,就可以确定一个投资组合。对于n种风险资产,有如下的一般公式:
投资组合收益:
投资组合方差: ,其中
其中E(ri)代表第i种资产的期望收益,σi代表第i种资产的风险(其收益的标准差)。而元素cov(i,j)则组成了通常所说的资产收益方差—协方差矩阵。从上面的公式可以看出,组合的方差是组合中各资产权重的二次函数。投资组合的风险用它的标准差来度量。
在使用电子表格时,采用那些便于输入的投资组合收益和方差表达式会很有好处。鉴于上面列出的求和公式非常不方便输入到表格中,这里采用两种可供选择的方法:(i)以Excel向量和矩阵乘法为基础的单元格公式;(ii)工作表中的用户定义函数。
首先来看单元格的计算公式,如果期望收益和投资组合的权重都用列向量形式来表示(分别用e和w表示,行向量则对列向量进行转置,即eT和wT),方差—协方差用矩阵V表示,那么原来的公式就可以改写成如下所示的简单矩阵公式。Excel数组函数可以很容易地计算出这些公式的结果。
矩阵表示 用Excel公式表示
投资组合收益:
投资组合方差:
图演示了对三种资产(国库券、债券和股票)进行的上述计算。表中数据代表1926年到1992年三种资产的表现。这些数据是以年百分比收益数据为基础,并应用Excel的STDEV和AVERAGE函数计算得到的。投资组合在三种资产间是这样分配权重的:40%:50%:10%。如果期望收益(C5:C7)和权重(I10:I12)的向量名分别为e和w,方差—协方差矩阵(C15:E17)为V,那么通过上面的Excel公式就可以计算出投资组合的收益和方差。因此,权重分别为40%:50%:10%的投资组合的期望收益为%(单元格I15),风险为%(单元格I16),后者是由方差(单元格I18)开方得到。
Risk&return with three assets:资产组合的风险和收益(由三种资产组成);
Asset Data:资产数据
Correlation Matrix:相关系数矩阵
VCV matrix:VCV矩阵
Portfolio weights:组合权重
【参照书中第104页的图】
图 计算包括三种资产投资组合的风险和收益
计算中使用的输入数据包括这三种资产收益的方差—协方差矩阵。矩阵的元素由资产收益之间的相关系数(单元格C10:E12)和标准差(D5:D7)计算得到。注意,图中求方差—协方差矩阵中每个元素的一般公式如下:
上式计算的是国库券和债券的协方差;与之对应的相关系数可以在单元格C11中找到,相应的标准差(国库券和债券)可以在区域B5:D7中找到。
计算投资组合风险和方差的第二种方法是用VBA编写用户定义函数。假设期望收益和组合权重向量分别用变量retvec和wtsvec表示,方差—协方差矩阵用vcvmat表示,于是可以创建两个名称分别为PortfolioReturn(RETVEC,wtsvec)和PortfolioVariance(wtsvec,vcvmat)的函数,用来分别计算所需的风险和方差。投资组合的风险就是PortfolioVariance函数计算结果的平方根。在Module1中的用户定义函数部分介绍了上述函数的具体程序。对于那些不熟悉Excel矩阵函数的人来说,使用用户定义函数比输入单元格公式更为简洁。
在第到节中,将以三种资产为例进一步解释均值-方差投资组合理论,并且,该理论可以很容易地推广应用到多种资产的情况。
组合的风险-收益表示
研究股票最重要的主题就是收益和风险。从图中可以清楚地看出,投资组合可以这样表示:在坐标平面上,x轴代表风险,y轴代表收益。图中显示了三种单个资产及其组合的所有可能位置。例如,风险最高的资产——股票,其位置在图的右上角(风险=%,收益=%),而国库券的位置则靠近原点(风险=%,收益=%)。在图所示的投资组合中,其主要的组成部分是国库券和债券(也有一部分股票),它在图中用标签为‘Fig1 PF’的点表示。虽然这个特定投资组合的期望收益为%,但是在保证该期望收益不变的情况下,微调不同资产的权重会得到一个风险最小的投资组合。风险最小的投资组合也被称为有效组合;所有代表有效组合的点组成的曲线即为有效边界。因此,所有权重达到最佳的投资组合均位于有效边界上,此时,在给定的目标收益下,该投资组合的风险最小。图中,这些有效组合或边界组合被标记为无约束边界。应该注意到这条边界包含一个唯一的最小方差点,因此,它也代表最小方差投资组合。在这里,投资组合的权重之和必须为1,但除此之外,没有更进一步的条件限制,因此此处使用了术语‘无约束边界’。
The Efficient Frontier:有效边界;
Expected Return:期望收益;
Risk(Standard Deviation):风险(标准差)
【参照书中第106页的图】
图 投资组合的风险-收益表示
在接下来的几节中,将从确定有效边界上的单个点入手,进而得到整个边界。然后将对投资者所持有的单个资产权重加上约束条件(这将会把整个投资组合的收益限制在一个较小的范围内),并计算出那些有约束边界上的点。注意,可以肯定的是,有约束边界上的点总是在无约束边界上或在其右侧,尽管由于图的大小有限,我们不能在图上清楚地看到这一点。
如果单个资产的权重没有额外的约束条件,我们可以通过黄和利曾伯格给出的公式(1988,第节)来得到有效投资组合。但由于用规划求解来得到有效组合比较容易解释,所以,接下来将首先介绍这种方法,然后将其扩展到单个资产权重有约束的情况。最后介绍得到整个有效边界的方法。
用规划求解得到有效点
资产的相关数据由图给定,要求得到目标收益为7%的有效投资组合。此问题就是要求出在目标收益为7%的情况下,收益方差最小的投资组合中,各种资产的权重分配情况。因为规划求解中有许多为最优化问题设计的反复迭代搜寻方法,所以这个标准的最优化问题用Excel中的规划求解可以很容易得到答案。既然投资组合的方差是权重的二次函数,我们将用规划求解来解决这个二次规划问题。
Using Solver to reproduce Unconstrained Frontier Portfolios:
利用规划求解重新产生无约束有效边界组合
Asset Data:资产数据
Correlation Matrix:相关系数矩阵
VCV matrix:VCV矩阵
Frontier Portfolio weights:有效边界组合权重
Ctrl+Shift+U to run Macro:按下组合键Ctrl+Shift+U可以运行宏
【参照书中第107页的图】
图 用于进行最优化的工作表EF1(在中)
Eppeb et al(1998)已明确指出了具有什么样代数结构的问题可以用二次规划来解决,以及如何用Excel的规划求解解决这些问题。因此,这里将在图的基础上,简明扼要地解释如何使用规划求解来解决这个投资组合问题。注意图中的区域名称,如I11:I12为change1,I15为portret1,等等。
在用规划求解进行计算时,需要输入可变单元格、作为最小化前提的目标单元格和规范的约束条件。这些约束条件用来控制可变单元格的取值范围。注意,要满足完全投资情况下的约束条件,只需在单元格I10中输入下面的公式即可:
于是,为了实现最优化而设置的可变单元格就是I11:I12,用区域名称来表示就是change1。需要最小化的目标单元格是收益的标准差(I16),命名为portsd1。在这个问题中有一个明确的约束,即期望收益(单元格I15,命名为portret1)必须等于目标值(在单元格I5中,命名为target1)。(注意:此处的区域名称在确定规划求解参数,以及以后编写VBA宏的过程中有着特殊作用。)
用规划求解处理上述过程的步骤如下:
1.调用规划求解-在菜单栏里选择工具,接着选择选项,点击规划求解;
2.指定规划求解参数对话框里的相应参数:
需要最优化的目标单元格(I16)
指定最大化或是最小化,并且指定可变单元格(I11:I12),如图所示;
3.点击添加来指定约束条件,然后点击确定(如图的右上方所示),这个约束条件保证了I15必须等于目标值(单元格I5);
4.点击选项,确保没有选中采用线性模型
5.计算并在工作表中得到相应结果。
【参照书中第108页的图】
图 用规划求解实现方差最小化
应用规划求解进行计算的结果如图所示。从最优权重分配比例中可以看出,需要持有权重为正的是债券和股票,国库券的权重则为负。从单元格I15可以得到,所求的有效组合在保证目标收益为7%的情况下,实现了%的最小标准差。改变单元格I5中给出的目标期望收益,再次调用规划求解,并按照前面所描述的步骤进行操作,将会得到另一个有效组合。(为了验证这一点,读者可以将I5中的目标收益从7%改为3%,接着调用规划求解计算得到新的投资组合权重。另一个练习是用来确定唯一的最小方差投资组合权重。如果去掉如图所示的约束条件portret1=target1,那么规划求解将给出期望收益为%、风险为%情况下各种资产的权重。)
既然现在对单个资产的权重没有其他约束条件,那么权重为负是可以接受的,这代表该资产被卖空。实际上,在图给出的例子中,期望收益为7%的边界组合中,国库券的持有比率为%。这意味着,为了构建该组合,必须借入数额为该组合价值一部分的资金,并为此部分资金支付利息,这就冲销了其他两种资产所获得的部分期望收益。
Using Solver to reproduce Unconstrained Frontier Portfolios:
利用规划求解重新产生无约束有效边界组合
Asset Data:资产数据
Correlation Matrix:相关系数矩阵
VCV matrix:VCV矩阵
Frontier Portfolio weights:有效边界组合权重
Ctrl+Shift+U to run Macro:按下组合键Ctrl+Shift+U可以运行宏
【参照书中第108页的图】
图 期望收益为7%的无约束边界组合
到现在为止,我们用最小化投资组合风险的方法得到了最优投资组合权重。同样,我们可以通过在某一给定风险水平下最大化投资组合收益的方法来确定投资组合的权重。这对使用规划求解来说好像是不同的问题,但用这种方法得到的最优权重,与前面最小化方差方法得到的有效边界投资组合的最优权重是一致的。
求有效边界(黄和利曾伯格的方法)
对单个资产的权重不加任何限制时,有效边界可以用数学方法推导出来。虽然一些更高级的教科书(例如埃尔顿和格鲁伯,1995),是通过反复迭代去求解一组联立方程来得到有效边界,但有一种更加简洁有效的方法。黄和利曾伯格(以下简称HL)给出了如何找到有效边界上的两个点,并在这两个点的基础上得到整个边界(直接应用布莱克的结论)。本节将利用他们给出的代数方法,计算时会用矩阵加以解释,并推广到投资组合有多种(多于三种)可选择资产的情况。下面将介绍如何使用Excel的数组函数在电子表格中求出有效边界。本节涉及的内容难度较大,初学者可以暂时略过,在完全理解本章后面的章节之后再来学习本节。
图中的表EF1HL很清楚地定义了区域名称,使得单元格中的公式更加容易理解。期望收益向量(C5:C7)被命名为e,权重向量(I5:I7)为w,A24:A26所表示的向量命名为u。C15:E17所代表的方差-协方差矩阵命名为V。如同前面解释的那样,投资组合的方差可以写成矩阵乘积wTVw的形式,结果放在单元格I11中。
用HL法寻找有效组合时,需要知道方差-协方差矩阵的逆,这里记为V-1。Excel中专门用来进行矩阵求逆的函数是MINVERSE。在使用数组函数时,需要在表中一个3×3的区域H15:J17中输入公式:
注意,对数组函数而言,在选择好单元格区域并输入正确的公式之后,必须按下组合键Ctrl+Shift+Enter来结束输入。(如果没有出现数组公式括号{},可以按下F2键重试一次。)
为了得到两个边界组合(标记为g和g+h),黄和利曾伯格首先计算出四个数值(A,B,C和D)。前三个值,A,B和C是由前面的几个向量和矩阵计算得到,第四个值D则是用A、B、C计算得到:
现在引入两个临时行向量I=V-1e和m=V-1u,结果分别存储在表中单元格区域C24:C26和D24:D26中,则上面四个矩阵乘法的表达式可以简化为:
单元格G23中有计算A的简单单元格公式:=MMULT(TRANSPOSE(u),I),其中调用了数组函数MMULT,因此计算A的公式必须作为一个数组函数来输入(输入B和C的公式时与A类似)。因为A、B、C和D的计算结果都是一个数值而非向量或矩阵,所以只需要用单个单元格来储存它们。
Using Algebra to reproduce Unconstrained Frontier Portfolios:
利用规划求解重新产生无约束有效边界组合
Asset Data:资产数据
Correlation Matrix:相关系数矩阵
VCV matrix:VCV矩阵;VCV inverse:VCV矩阵的逆;
Portfolio weights:组合权重
Finding weights,g and h,to generate points on the frontier:
寻找权重,g和h,从而生成有效边界上的点;
Generating Frontier Portfolios,using g and h:
利用g和h生成有效边界
【参照书中第110页的图】
图 用黄和利曾伯格的方法直接得到解析解
为计算边界组合g(期望收益为0%)和边界组合g+h(期望收益为100%)中各种资产的权重,只需计算如下所示的两个公式:
在输入计算g的公式时,需要先选择一个3×1的列向量区域,因为这是一个数组公式。输入计算h的公式时与g类似。图中的单元格I24:I26和K24:K26给出了这两个边界组合的权重。
于是向量g(124%,%,%)给出了国库券、债券和股票的权重,这些权重确定了有效边界上期望收益为0%的投资组合。同样,向量g+h给出了另一组国库券、债券和股票的权重,这些权重确定了有效边界上期望收益为100%的投资组合。把上面得到的向量g和h进行线性组合g+h*T,可以得到有效边界上给定期望收益为T的投资组合权重。例如,从图的33到38行可以看出,期望收益为7%的投资组合中包含%的国库券、%的债券和%的股票(单元格D36:D38)。也就是说,为构造期望收益为7%的最小风险投资组合,需要买进债券和股票,同时卖空国库券。这些用HL方法得到的结果与图中在没有其他权重约束情况下用规划求解得到的结果是一致的。
边界上的其他点可以用数组函数g+h*T计算得到,其中T是该点的期望收益。用这种方法可以得到整个有效边界。例如,在Excel中,一个模拟运算表可以将一系列期望收益(从0%到10%)作为输入,计算出相应的投资组合风险和收益,然后可以在此表的基础上画出一个XY(散点)图。在图中可以看到,用‘无约束边界’标记的所有点都来自于上述的模拟运算表。
显然,在电子表格中为得到两个有效组合权重所进行的计算非常复杂。这就是我们采用VCA用户定义函数来简化公式输入的原因。第节将演示黄和利曾伯格的计算是如何通过编程来实现的。HLPortfolioWeights函数有四个重要变量:expret,retvec,vcvmat和rf(这里是-1)。expret是该有效边界组合的期望收益,retvec是期望收益向量,而vcvmat是方差-协方差矩阵。该函数可以得到投资组合的权重。在图中,用此用户定义函数得到的权重放在单元格E36:E38中。
有约束边界组合
如果给单个资产的权重加上一些约束条件(例如权重必须非负),此时解析方法就不再适用了。但是,使用规划求解仍可以得到最优权重。为了与前面的情况区别开来,这里把这种情况下得到的投资组合叫做‘有约束’边界组合。图展示了从表EF2中摘录的部分信息,从表中可以看出,可能的组合权重放在单元格H8:J8中(命名为portwts2),其上面一行是所能允许的最小权重,下面一行则列出了所能允许的最大权重(分别命名为portmin2和portmax2)。用规划求解可以解决上述约束条件下的边界组合问题,具体的操作如图所示。
Using Solver to generate the constrained Frontier:
利用规划求解生成有约束有效边界
Asset Data:资产数据
Correlation Matrix:相关系数矩阵
VCV matrix:VCV矩阵
Constraints on Frontier Portfolio weights:对有效边界组合权重的约束
Ctrl+Shift+U to run Macro:按下组合键Ctrl+Shift+U可以运行宏
【参照书中第111页的图】
图约束条件下的投资组合权重最优化-工作表EF2
【参照书中第112页的图】
图 权重有约束时的规划求解操作过程
用规划求解得出的边界组合中,各种资产的权重如下:国库券%,债券%,股票%。这个边界组合的期望收益率仍为%,但是现在给权重加上了约束条件:权重不能为负,国库券、债券的最大权重分别为10%和20%,此时整个组合的标准差为%。这与无约束条件下得到的只有%的方差相比,略有增加,这也说明了添加约束条件后得到的结果通常要比无约束时差。
针对不同的目标期望收益,分别运用规划求解进行计算,可以得到有约束边界上的一系列点(见图)。使用宏的技巧将在节中介绍。在上述那些权重的约束条件下,分别选取从%到%的一系列目标期望收益。[读者可以通过使用规划求解来计算使期望收益最小(大)化的权重,从而验证这些数字]假设我们要进行11次最优化,上面所选取的一系列收益就需分成11个目标收益,然后应用11次规划求解,就可以得到期望收益在所选范围(从%到%)内的边界组合。
Constrained Frontier Portfolios:有约束有效边界组合
【参照书中第112页的图】
图 有约束边界组合的权重
图列出的投资组合在图中被标记为有约束的点(Constrained Points),最后一个组合相当于只投资于股票。
总而言之,确定风险投资组合权重的常用方法,就是使用规划求解进行最优化,其本质上是一个数值反复迭代的过程。但是,在没有对权重加上约束条件(除权重之和为1的条件之外)的情况下,可以用HL给出的一系列公式得到最优权重,并可以用第节中介绍的用户定义函数来进行具体计算。
无风险资产和风险资产的结合
从本节到节主要介绍有关用风险资产构造投资组合的一些其他知识。但这一部分的理论性较强,可以看作是一个附录材料。本部分的重要性在于:这里推导出来的公式可以用来解决‘三类常见组合问题(three generic portfolio problems)’(Taggart,1996),并可以编写成用户定义函数。这些函数可以为第7、8章的一些问题提供明确的解答。这里我们将介绍无风险资产(风险为零的资产)的基本概念,为下一章推导资本资产定价模型打下基础。
在讨论完由三种(或三种以上)风险资产构造的投资组合,在给定目标收益情况下,如何得到风险最小的有效组合之后,这里将扼要介绍在投资中应如何权衡风险和收益。在决定如何在一个由风险资产组成的投资组合与一种无风险资产之间分配投资时,权衡风险-收益是需要考虑的主要因素。这一想法是在解决下面的三类常见组合问题时产生的:
问题一:一种风险资产和一种无风险资产的组合;
问题二:两种风险资产的组合;
问题三:一种无风险资产和一个风险投资组合的组合。
在Generic表中演示了解决上述问题的电子表格方法。
这里需要定量分析个人对风险和收益的权衡,因此从经济学中借用了效用函数这个概念。假定个人拥有如下的效用函数:
其中,投资者的风险厌恶系数A的值越大,由投资风险带来的、需要从投资预期收益中减去的惩罚项就越大。实验表明,A的值通常在2到4之间。下面的研究中,我们选择了A=3。
既然接下来的几节都是研究两种资产的情况,因此在此处把节给出的投资组合收益和风险的一般公式修改为只涉及两种资产的情况:
投资组合收益:
投资组合风险:
其中,两种资产收益的协方差cov(1,2)可以用其相关性来度量,即cov(1,2)=corr(1,2)σ1σ2。无风险资产的收益用rf来表示,其方差为零,与其他风险资产的协方差也为零。整个投资组合的风险等于方差的平方根。
问题一 一种无风险资产和一种风险资产的组合
三类常见组合问题中的第一个,是指由一种无风险资产(asset0)和一种风险资产(asset1)组成的组合,收益不相关。图给出了这两种资产的具体情况:无风险利率是1%,asset1的风险和收益状况与前面几节所研究的投资组合中的债券是一样的。我们把投资于asset1的资金比例(单元格H14)从0%变化到100%,这就得到风险收益图上连接代表asset0的点到代表asset1的点的一条直线。图上标明了这条直线。
Generic Portfolio Problems:常见组合问题;
Problem One:risk-free asset and 1 risky asset:
问题一:无风险资产和一种风险资产的组合
Asset Data:资产数据;
Corr Matrix:相关系数矩阵;
VCV Matrix:VCV矩阵;
Risk aversion coefficient:风险厌恶系数;
Optimal Portfolio:最优组合
【参照书中第114页的图】
图用电子表格解决问题一(表Generic)
Problem One:问题一;
Portfolio return:组合收益;
Portfolio risk(standard deviation):组合风险(标准差);
Portfolio utility:组合效用
【参照书中第114页的图】
图 问题一中效用、收益与风险的关系图
个体投资者将会根据他们的风险厌恶系数(单元格K6中)在直线上选择一个特定的、符合自己要求的点。图还给出了假定风险厌恶系数为3时投资者的效用曲线,上面不同点所代表的投资组合中asset1的比例是不同的(图上表示为一系列离散点,其纵轴刻度在图的右边)。效用值从(所有资金都投资于asset0)开始,随着投资于asset1的资金比例增加而平稳增加到最优点,接着开始减少到(所有资金都投资于asset1)。
直线上的最优点就是效用最大点。可以通过下面的公式计算出asset1的权重(列在单元格H14中),得出最优点:
在这个例子中,最优投资组合中包括36%(%)的风险资产投资,总效用是(也就是说,等于该投资者获得大小为%的确定性等价收益时所得到的效用)。同样可以设计一个名为Prob1OptimalRiskyWeight的用户定义函数,以此来求出权重(单元格H15)。这将在节中进行介绍。
注意,最优投资组合中风险资产的比例(与asset0投资比例的变动情况相反-因为二者之和为1)是由投资者的风险厌恶程度决定的。一个风险厌恶系数更大(例如4)的投资者将会把其最优投资组合中的风险资产比例减少到27%。
问题二 存在两种风险资产的组合
在问题二中,存在两个风险资产。在后面的分析中,假设这两种资产的情况分别与前面例子中给出的债券和股票相同,则它们的收益存在微弱的正相关关系。具体的数据列在图中,其中单元格D25给出了相关系数。
Problem Two:2 risky asset and no risk-free asset:
问题一:两种风险资产的组合
Asset Data:资产数据;
Corr Matrix:相关系数矩阵;
VCV Matrix:VCV矩阵;
Risk aversion coefficient:风险厌恶系数;
Optimal Risky Portfolio:最优风险组合
Minimum Variance Portfolio:最小方差组合
【参照书中第115页的图】
图用电子表格解决问题二(表Generic)
改变asset1的权重,可以描出整个边界(见图所示的连续曲线)。由代表两种风险资产不同组合的风险收益点连接而成的这条曲线,很好地说明了投资分散化的好处。例如,如果一个投资者最初把所有资金全投资于asset1,那么他把部分资金转投到asset2上会增加其所有投资的收益。他的投资组合风险也会因此而减小,只有在asset2的风险比asset1要大时才会出现例外。
上述资金从asset1转投到asset2的过程将持续减小整个投资组合的风险,直到组合方差达到最小,此时asset1的权重为%,组合的标准差为%。计算最小方差投资组合的公式与投资者的风险厌恶系数无关,甚至可以认为该投资者对风险是完全厌恶的。
最小方差投资组合中w1的计算公式为:
为简便起见,asset1的权重记为w1mv(单元格H24中的%),asset2的权重就是(1-w1mv)。所以在asset1的权重为%时,投资组合的风险最小(%)。应该注意到,此时组合的风险小于两个风险资产各自的风险。
对风险更加偏好的投资者可以减少对asset1的投资,增加对asset2的投资。图给出了一个风险厌恶系数为3的投资者从不同的投资组合获得的效用情况(图上表示为一系列离散点,其纵轴刻度在图的右边),从中可以看出效用一直增加,到风险大约为15%时达到最大值。这个投资者愿意承受更大风险,在他的最优投资组合中,asset1的权重为%,组合的标准差为%。
通过一系列的数学运算,我们可以得到asset1的最优风险权重表达式如下所示:
Problem Two:问题二;
Portfolio return:组合收益;
Portfolio risk(standard deviation):组合风险(标准差);
Portfolio utility:组合效用
【参照书中第116页的图】
图 问题二中效用、收益与风险的关系图
在单元格H29中给出的权重确定了最优风险投资组合。最优投资组合中asset1的权重等于最小方差投资组合中asset1的权重加上一个额外部分。这个额外部分与投资者的风险厌恶系数有关,并且可以看作是投资者的投机需求。最优风险投资组合的效用是(这相当于投资者获得大小为%的确定性等价收益时得到的效用)
同样,最优投资组合的权重可以通过用户定义函数很容易计算出来。节将给出用来计算该权重的Prob2OptimalRiskyWeight函数。
问题三 一种无风险资产和一个风险投资组合
常见组合问题中的最后一个问题研究一个无风险资产和一个风险投资组合的情况。通常分两步解决。首先计算风险投资组合中各种风险资产的最优权重(问题二的特殊情况)。其次确定投资如何在无风险资产和最优风险投资组合之间分配(实质上是问题一)。图给出了详细的计算过程。
首先,在不考虑无风险资产的情况下,确定投资如何在asset1和asset2之间达到最优分配。前面一节给出了只考虑风险资产时两种资产的最优分配比例大约为35%:65%。假设最优风险投资组合(此处记为R)的预期收益为E(rR)、标准差为σR,其中asset1的权重为F1。在第二步引入无风险资产后,我们需要确定合适的F1使投资组合R的收益-风险比率最大,也就是说,选择合适的F1使下式达到最大:
简单计算后(也可以像Bodie那样从几何上直接观察图形),我们会发现当连接有效边界上的投资组合与无风险资产的直线的斜率在直线与边界相切时达到最大。为了计算切点处各资产的权重,我们需要使用超额收益(即超过无风险利率的那部分收益),分别定义为E(R1)=E(r1)-rf和E(R2)=E(r2)-rf。最优投资组合权重的计算公式为:
其中E(R1)和E(R2)是超出rf的那部分预期收益。
注意:最优风险投资组合的权重与风险厌恶系数无关,而与无风险利率有关。
Problem Three: risk-free asset and 2 risky asset:
问题一:无风险资产与两种风险资产的组合
Asset Data:资产数据;
Corr Matrix:相关系数矩阵;
VCV Matrix:VCV矩阵;
Risk aversion coefficient:风险厌恶系数;
Optimal Risky Portfolio:最优风险组合
Solved in 2 stages:采用两步解决
Stage 1:Optimal Risky Portfolio(‘revised’ Problem2),第一步:最优化风险组合(‘修正的’问题二)
Independent of A: 与A无关
Stage 2:Optimal Portfolio(using Problem1),第二步:最优化整个组合(利用问题一的结论)
Depends on A :与A有关
Optimal Portfolio:最优组合
【参照书中第117页的图】
图用电子表格解决问题三(表Generic)
在单元格H41中应用该公式可以得到%的权重,这意味着在最优风险投资组合中asset1和asset2的权重分别为%和%。
第二步要从第一步得到的最优投资组合出发,考虑如何在该组合和无风险资产之间分配投资以使效用最大化。从而,节给出了风险投资组合的权重为[E(rR)-rf]/[AσR2],无风险资产的权重则为1-[E(rR)-rf]/[AσR2]。此处投资组合的权重与风险厌恶系数有关。
现在得到的最优投资组合包括%的风险投资组合和%的无风险资产,其效用从第一步得到的增加到。[此处风险投资组合的权重也可以用Prob1OptimalRiskyWeight函数求出(见单元格H50)]
接下来把68%的投资分配到每种风险资产上,我们就得到如单元格H52:H54所示的权重。同样,用户定义函数Prob3OptimalWeightVec也可以给出整个最优投资组合的权重向量(见单元格I52:I54),这将在节加以介绍。
从图可以看出,边界线上的每个点都代表asset1和asset2的某种组合。在引入无风险资产之后,在连接风险投资组合的边界和无风险资产的直线与边界相切时,该切点就是唯一的最优投资组合。(边界上的这个切点可以仿照问题二的解决方式加以解释。)从左到右来看这条与边界相切的直线,开始时所有资金均投资于无风险资产,在切点无风险资产的权重为0,接着到达最优风险投资组合的权重为150%、而无风险资产的权重为负50%的点。(因为此处对无风险资产的权重没有任何约束。)第二步(和问题三的答案)需要确定投资者的最优投资组合在这条切线上的什么位置。这里又一次画出了一系列的点表示投资者的效用。从图上可以看出,在我们选择的风险厌恶系数下,效用将在风险约为%时达到最大化。
Problem Three:问题三;
Portfolio return:组合收益;
Portfolio risk(standard deviation):组合风险(标准差);
Portfolio utility:组合效用
【参照书中第118页的图】
图 问题三中效用、收益与风险的关系图
现在,在某种意义上,从问题三到下一章推导出资本资产定价模型只需要再前进很小的一步,最主要的难点将是如何将所有个体投资者的信心汇总,从而给出资产定价理论。
Module1中的用户定义函数
表Module1中给出了Equity1中用户定义函数的有关程序。这些程序演示如何使用用户定义函数来处理数组,包括数组输入、数组函数、用数组和标量进行计算、数组形式输出等操作。例如,PortfolioReturn函数的程序是:
Function PortfolioReturn(retvec, wtsvec)
returns the portfolio return
If (retvec) = (wtsvec) Then
If <> Then
wtsvec = (wtsvec)
End If
PortfolioReturn = (retvec, wtsvec)
Else
PortfolioReturn = -1
End If
End Function
上面的程序是用来检查计算中所用到的数组维数是否相等。两个数组维数相同时可以用SumProduct函数计算两者乘积,其结果为一个标量。所以,计算时首先检查数组retvec和wtsvec包含的元素个数是否相同,并且当列数不同时还要对数组wtsvec进行转置。如果这些数组包含元素的个数不相等,上面的函数就会返回一个错误值-1。有了上述检查,用户在定义函数时,输入行向量或列向量都可以,而不拘泥于某种特定形式的向量。
使用HLPortfolioWeights函数(Module1中有该函数的相关程序)处理有关数组函数时需要特别小心。一个关键问题是要注意函数所用数组的维数。在VBA中一维数组被储存为行向量,同样,从用户定义函数得到的一维数组也将以行向量的形式输出到工作表中。我们将在下面的计算中遵守这个规则,确保用户定义函数得到的VBA一维数组以行向量的形式输出到Excel中。当然,我们也希望用户定义函数中的向量可以采取另外一种形式—列向量。例如,在某些情况下为了保证变量uvec和retvec是列向量,可以对它们进行转置。
数组函数中的中间变量同样需要按照这个规则进行处理:
Dim I As Variant, m As Variant
I = (vinvmat, retvec)
m = (vinvmat, uvec)
变量I和m是从一个数组函数得到的,并且将会作为其他数组函数的输入变量,所以将它们声明为Variant型,即没有确定维数的变量。在使用MMult函数进行计算的过程中,vinvmat是一个n×n矩阵,而retvec是一个n×1向量。因此计算得到的数组将是一个n×1(列)向量。
接下来要解决的问题是计算四个数值a,b,c和d。前三个数可以通过一个数组函数求出,d则可以根据前三个数计算得到。例如a可以通过单位向量数组uvec和数组1求出:
MMult函数把一个1×n(行)向量与一个n×1(列)向量相乘,得到一个1×1数组(标量)。在Excel中这一点很容易做到,但在VBA中我们还需要使用Sum函数把这个数组结果转化为一个标量。
最后的问题是如何处理数组的单个元素和标量之间的混合运算。在Excel中,这一点也很容易做到,具体情况参见列在单元格I24:I26中的向量g。但是,在VBA中最好用一个循环来实现:
Dim wtsvec() As Variant
n = (retvec)
ReDim wtsvec(n)
for I = 1 to n
gi = b * m(I, 1) – a * i(I, 1)
hi = c * i(I, 1) – a * m(I, 1)
wtsvec(i) = (gi + hi * expret) / d
next I
由于是通过计算出每个元素后得到wtsvec,于是,将其声明为Variant(),但最终还是要用ReDim语句定义它的维数。循环中,可以用数量直接乘以数组中的每个元素。
Module1中用于解决三类常见组合问题的函数
Prob1OptimalRiskyWeight函数有四个输入,它是直接由节给出的公式转化为VBA程序而得到的。四个必要的输入包括风险资产的收益和风险(r1和sig1),无风险利率(rf)和风险厌恶系数(rraval):
Function Prob1OptimalRiskyWeight(r1, rf, sig1, rraval)
’returns risky optimal weight when combined with risk-free asset
Prob1OptimalRiskyWeight = (r1 - rf) / (rraval * sig1 ^2)
End Function
接下来介绍的函数是Prob2OptimalRiskyWeight,它可以用来解决问题二和问题三。该函数共有7个输入:r1,r2,sig1和sig2是两种风险资产的收益和标准差;rraval是投资者的风险厌恶系数,无风险利率是rf。问题二中的无风险利率应该设为0:
Function Prob2OptimalRiskyWeight(r1, r2, rf, sig1, sig2, corr12, rraval)
’returns optimal weight for risky asset1 when combined with risky asset2
’for case with no risk-free asset, enter Value of rf <= 0
Dim cov12, var1, var2, minvarw. w, xr1, xr2
cov12 = corr12 * sig1 * sig2
var1 = sig1 ^ 2
var2 = sig2 ^ 2
’first look at case with no risk-free asset
If rf <= 0 then
minvarw = (var2 – cov12) / (var1 + var2 –2 * cov12)
w = minvarw + (r1 – r2) / (rraval * (var1 + var2 –2 * cov12))
’then look at case with risk-free asset
Else
xr1 = r1 – rf
xr2 = r2 – rf
w = xr1 * var2 – xr2 * cov12
w = w / (xr1 * var2 + xr2 * var1 – ( xr1 + xr2) * cov12)
End If
Prob2OptimalRiskyWeight = w
End Function
在上面计算最优权重的程序中,cov12表示协方差。变量minvarw是最小方差权重w1mv, w是最优投资组合的权重w1opt。
问题三的函数中需要使用问题一和问题二所使用的函数。顾名思义,Prob3OptimalWeightVec函数最后将以数组的形式输出三个最优权重。下面给出了完整的程序,并对某些步骤进行了说明:
Function Prob3OptimalWeightVec(r1, r2, rf, sig1, sig2, corr12, rraval)
’returns optimal weight for risk-free asset and 2 risky assets
’uses Prob2OptimalRiskyWeight fn
’uses Prob1OptimalRiskyWeight fn
Dim w0, w1, w2, rr, sigr
w1 = Prob2OptimalRiskyWeight1(r1, r2, rf, sig1, sig2, corr12, rraval)
w2 = 1 – w1
rr = w1 * r1 + w2 * r2
sigr = Sqr((w1 * sig1) ^ 2 + (w2 * sig2) ^ 2 + 2 * w1 * w2 * corr12 * sig1 * sig2)
w0 = 1 - Prob1OptimalRiskyWeight(r1, rf, sig1, rraval)
w1 = (1 – w0) * w1
w2 = (1 – w0) * w2
Prob3OptimalWeightVec = Array(w0, w1, w2)
End Function
不考虑无风险资产时,问题就退化为问题二,asset1和asset2的权重是这样计算的:
w1 = Prob2OptimalRiskyWeight(r1, r2, rf, sig1, sig2, corr12, rraval)
w2 = 1 – w1
知道了这些权重之后,我们就可以计算风险投资组合的收益和风险:
rr = w1 * r1 + w2 * r2
sigr = Sqr((w1 * sig1) ^ 2 + (w2 * sig2) ^ 2 + 2 * w1 * w2 * corr12 * sig1 * sig2)
上面这些数字可以用来确定无风险资产的权重(本质上是问题一),因此需要使用Prob1OptimalRiskyWeight函数。
模块M中的宏功能
本节将介绍可以求出有效边界(前面的几节中是用规划求解求出的)的宏。规划求解是Excel的一个插件,在安装之后才会出现在工具菜单中。另外,为了在VBA中使用规划求解功能,这个模块需要使用 插件。
Sub Efffrontier1()
SolverReset
Call SolverAdd(Range(“portret1”), 2, Range(“target1”))
Call SolverOk(Range(“portsd1”), 2, 0, Range(“change1”))
Call SolverSolve(True)
SolverFinish
End Sub
Efffrontier1宏包含对规划求解的一个简单应用。SolverAdd函数加上了必须的简单约束(数值2表示了这个等价约束),接着用SolverOk函数提出了要解决的问题(这里的数值2说明要进行最小化)。SolverSolve函数用来解决这个问题(参数True的作用是隐藏结果屏),程序结尾部分的SolverFinish函数得到问题的答案并显示在工作表中。
严格的说,程序中的Call并不是必需的,但是我们建议,在程序中使用了需要参数的子程序时,最好使用Call。在使用这个语句时,参数必须用括号括起来。
Efffrontier2宏相当复杂,这是因为它在一个循环中反复使用了规划求解功能,最后得到了整个有效边界。循环的次数需要预先确定。最重要的是,在循环中我们要使用尽量少的程序。规划求解问题是在循环外建立的,在循环中需要使用SolverChange函数改变约束条件等式的右端(目标预期收益)。循环中,每次规划求解迭代运算的结果将会用PasteSpecial命令复制到工作表的一个特定区域。下面就是该循环的程序:
Do While iter <= niter
Call SolverSolve(true)
SolverFinish
Range(“portwts2”).copy
Range(“effwts2”).Offset(iter, 0).PasteSpecial Paste:=xlValues
Range(“priter2”) = Range(“priter2”).Value + pradd
’amend portret constraint in Solver
Call SolverChange(Range(“portret2”), 2, Range(“priter2”))
iter = iter + 1
Loop
在这个宏的开始部分用了两次规划求解:第一次是计算在给定的约束下可以得到的投资组合最小收益(用SolverOk函数中给出的数值2表示);第二次是计算最大收益(用SolverOk函数中给出的数值1表示)。在这个收益范围内将选取若干个预先确定数目的目标收益,针对每个不同的收益求出并储存其相应的边界组合权重。
SolverReset
’first calculate portfolio min return given constraints
Call SolverAdd(Range(“portwts2”), 3, Range(“portmin2”))
Call SolverAdd(Range(“portwts2”), 1, Range(“portmax2”))
Call SolverOk(Range(“portret2”), 2, 0, Range(“change2”))
Call SolverSolve(True)
SolverFinish
prmin = Range(“portret2”).Value
’then calculate portfolio max return given constraints
Call SolverOk(Range(“portret2”), 1, 0, Range(“change2”))
Call SolverSolve(True)
SolverFinish
小结
马可维茨创建的投资组合最优化理论是本书整个股票部分的理论基础。本节介绍了使用Excel的数组函数功能,很容易把两种资产投资组合均值和方差的基本计算公式推广到多种资产的情况。由于数组函数可以实现矩阵乘法和矩阵求逆,我们可以用Excel求出黄和利曾伯格的解析解,从而得到有效边界。上述步骤可以通过用Excel工作表直接计算或用户定义函数的方式实现。
虽然上述理论很重要,但实际应用时需要诸如规划求解之类的工具进行计算。本节证明了在无约束情况下用规划求解得到的解与HL的解析解是一致的,同时也给出了在单个资产权重有约束的情况下如何使用规划求解得到正确解答。另外,本节还介绍了在用工作表直接计算和用宏时,如何使用规划求解工具。
下一章我们将介绍金融理论的另一个重要发展-资本资产定价模型和模型中β值的作用。接下来,我们将在股票收益服从对数正态分布的假设下预测单个股票和投资组合的未来价值和风险值。
参考文献
Bodie, Z., A. Kane and A. J. Marcus, 1996,Investments, 3rd Edition, Richard E. Irwin, Englewood Cliffs, NJ.
Elton, E. J. and M. J. Gruber, 1995, Modern Portfolio Theory and Investment Analysis, John Wiley & Sons, Chichester.
Eppen, G. D., F. J. Gould, C. P. Schmidt, J. H. Moore and L. R. Weatherford, 1998, Introductory Management Science, Decision Modeling with Spreadsheets, 5th edition, Prentice Hall, New Jersey.
Huang, C. and R. Litzenberger, 1988, Foundations for Financial Economics, North Holland, New York.
Taggart,R. A., 1996, Quantitative Analysis for Investment Management, Prentice Hall, New Jersey.
第7章 资产定价
本章的研究将从单个投资者(微观角度)转移到包括多种资产的整个市场,并且观察所有投资者的行为(宏观角度)。其中最主要的区别是我们原来描述的是个体行为,而现在我们要把所有投资者作为一个整体来考虑,总结他们的行为。只有这样,才能研究对金融资产的定价。资本资产定价模型(CAPM)是在60年代由金融学院派发展起来的。其基础是第6章介绍的投资组合均值-方差分析。其中一条重要的结论是:市场价格只反映了投资组合的一部分风险。市场价格所反映的那部分风险是与市场表现相关,通常由β值来度量。在Bodie et al.(1996)著作的第八章中详细讨论了CAPM的有关背景知识。
本章一开始就引入了单因素模型,并且解释了如何计算相关风险,其中需要特别注意的是β值和单个资产收益之间的方差—协方差矩阵是如何估计的。本章用到的核心数学方法就是回归,它主要用于估计单个资产的β值。一个资产的β值度量了该资产的收益与整个市场收益的相关性。资产的β值是描述资产收益与市场相关性的唯一工具。但是,在单因素模型中,协方差也是很容易计算的。工作簿中展示了估计过程是如何实现的,其中还包括许多为了减少计算量而编写的用户定义函数。
上一章,我们在用效用函数刻画单个投资者偏好的假设下,推导出马可维茨的均值-方差模型。分析中并没有考虑资产收益分布。同样,可以在忽略投资者偏好而假设资产收益服从对数正态分布的条件下,通过均值-方差模型得到与前面完全相同的理论结果。
在假设收益对数服从正态分布的条件下,可以得到水平财富(horizon wealth)预测值和风险值的解析解。本章同时给出了使用上述技巧的实例。在某种意义上,我们接受马可维茨关于收益和风险可以与正态分布的均值和方差联系起来的观点,于是我们可以借用统计学的一些已有结论。另外,为了更容易实现在资产收益的正态和对数正态形式之间进行反复转换,我们对刻画两种分布联系的有关理论进行实例解释。
单因素模型
单因素模型为推导资产定价模型时更好地理解收益和风险有很大帮助。模型假设股票i的收益和指数I的收益之间存在一种线性关系。分别用Ri和RI表示股票i和指数I的超额收益(即超过无风险收益的部分)时,模型可以写成:
其中αi和βi是模型的参数,于是股票收益可以分为两部分:系统部分(αi+βiRI)和残差部分ei。系统部分的αi是指数I超额收益为0时的股票收益,而βiRI则是与指数密切相关的部分。于是,βi度量了股票对指数运动所作出的反应。收益ei与指数无关,仅仅是股票i所特有的。通常假设ei是一个随机误差项,其数学期望为0,即E(ei)=0,所以股票i的预期超额收益为:
如果指数是一个市场指数(例如英国的FTSE100指数和美国的S&P500指数),此时收益的系统部分就是‘与市场相关的’,残差部分就是‘公司特有的’。参数β就度量了股票相对市场的敏感性。
用股票和指数的超额收益(例如用60个月的收益数据)数据可以估计回归参数αi和βi。同样可以得到残差方差σ(ei)2的估计值。
单因素模型同样可以用来分解风险(为了与第六章保持一致,风险用方差来表示)。股票i收益的方差可以分解为两部分:第一部分反映系统风险,第二部分反映该股票特有的风险:
此结论极大简化了为得到方差-协方差矩阵所作的计算,具体计算过程将在第节中介绍。
估计β系数
估计β系数时,最好使用对数收益数据(实际上是超额收益的对数)。表Beta给出了如何通过对股票A和指数的月度收益(图的B和C列)进行回归来估计单只股票的β系数。这里使用的指数是FTSE100,共有60组股票A和指数的月度收益数据。β系数(更严格地说是‘未校正’的β值)就是以股票超额收益作为被解释变量、指数收益作为解释变量进行回归得到的斜率。
Estimating Beta Coefficients using Ln Xs Returns:利用对数收益数据来估计Beta值
【参照书中第126页的图】
图 用股票A的收益对市场指数的收益进行回归来估计β值
Excel提供了许多可以用来估计斜率值的方法。单元格I7中的公式=SLOPE(B6:B65,C6:C65)采用了Excel的SLOPE函数来估计斜率βi。类似地,单元格I6中的INTERCEPT函数计算了回归方程中的截据项αi。在方程中代入αi和βi的估计值可以得到:
‘拟合(Fitted)收益’=-+×指数收益
E列和F列分别给出了在每一个指数收益下得到的‘拟合(fitted)’股票收益和‘残差’(即实际收益减去‘拟合’股票收益的差)。残差的大小是由残差标准误差(单元格I9)度量的。残差标准误差可以通过另外一个Excel函数STEYX(它与SLOPE函数的输入是相同的)得到。图为股票收益和指数收益的散点图及相应的回归直线。实际收益偏离这条直线(因此与指数不相关)的程度被称为这只股票的特有风险。它是前面提到的σ(ei)的最佳估计。
Beta Regression:Beta值回归
Share Return:股票收益
Index Return:指数收益
【参照书中第127页的图】
图 股票收益与市场收益的散点图(包括回归直线)
另外一种进行回归分析的方法(虽然是以静态的形式)是使用工具菜单,然后选择数据分析,点击回归,就得到如图所示单元格K6下的回归结果。截距和斜率在单元格L22:L23中,残差标准差在单元格I12中。
使用Excel的LINEST函数可以得到更简练的回归结果。与上面只能给出一个静态的、不能改变结果的方法不同,此函数可以进行动态调用。LINEST函数是一个数组公式,这里有5行、2列,因此需要预先选择好一个5×2的区域来输入该公式,并输出回归结果(接下来不要忘记按下Ctrl+Shift+Enter组合键):
这些结果显示在图的区域O6:P10中,对每个数字的解释标签列在N和Q列中。
LINEST数组中的元素可以使用INDEX函数得到(见单元格I11,I12,I14和I15)。例如,在单元格I14中,β系数可以用下面公式得到:
在单元格I15中,β系数的标准差可以用下面公式得到:
在表Beta中我们可以清楚地看到,单元格I19中用STEYX函数给出了股票的特有风险(以年度标准差表示),单元格I23(残差平方和再除以n-2)同样给出了股票的特有风险。股票的年度风险等于月度风险乘以得到。
Output from Tools/Data Analysis/Regression:
用菜单操作(工具/数据分析/回归)得到的结果
Output from Linest
利用Linest函数得到的结果
【参照书中第128页的图】
图 应用工具栏回归命令得到的结果
在估计β值的过程中,还需要对它进行校正,校正后的β值才能用来预测股票的收益。表中的β值是从60组月度观测数据中得到的样本估计值。校正β值的原因是基于下面的假设:整个市场上所有股票用价值加权的真实β值之和必须等于1,并且较高的β样本估计值在要预测的时段里有可能降低,同样,较低的β样本估计值在要预测的时段里有可能升高。因此β的样本估计值将向的总体均值调整。调整的幅度依赖于β样本估计值的方差(等于β样本估计值标准差的平方)与总体β值方差的相对大小(假设为)。这个比率就是均值回复因子(mean reversion factor)。例中,见图,回归的β样本估计值用一个大小为32%的均值回复因子进行调整,从而向整个市场的均值靠拢。校正后的β值显示在单元格O31中,大小为。均值回复因子也可以用来调整β样本估计值的标准差。下面摘录的工作表显示,用户定义函数给出了一种更方便地调整β样本估计值的方法。
Adjusting sample beta for mean reversion:
为均值回复因子调整β样本
【参照书中第129页的图】
图 为了预测股票收益而校正样本β值
资本资产定价模型(CAPM)
前面一章,我们发展了组合优化理论,并且解决了第三个问题-投资者如何在最优风险投资组合和无风险资产之间分配投资资金。CAPM模型是基于下面假设建立起来的模型:只存在一个最优风险投资组合;这个组合就是市场组合,市场组合中各种股票的权重等于其价值占整个市场价值的比例。我们已经从只考虑单个投资者的微观角度转到了考虑所有投资者的宏观角度,这样就可以介绍资产定价的有关理论了。CAPM理论认为,β值,即股票收益和市场收益的协方差,是市场定价的唯一因素。另一方面,投资者承担单只股票的特有风险将得不到任何回报,因为CAPM理论认为,对于所有股票而言,截据α的期望值为0。
CAPM是研究与市场预期收益相关的资产预期收益理论。此理论认为:对于一个风险完全分散化的投资者,影响股票i预期超额收益E(Ri)的唯一因素就是股票的系统风险(用βi来度量)。对所有股票而言,α的期望值为0。用公式表示就是:
其中,βi等于cov(Ri,RM)/σM2,E(RM)是市场的预期超额收益。βi E(RM)通常被称为收益的“CAPM基准”。
假设在市场组合之外存在一个风险资产x,通过解问题三可以推导出CAPM,于是,问题三的答案是,资产x的权重为0。在前面一章中,我们假设投资者的效用函数为二次的(也就是说,形式为U=E(rp)σP2),并在此基础上发展了投资组合理论。CAPM理论可以不使用效用的概念,而通过资产收益服从对数正态分布这个假设推导出来。我们将在节观察一只普通股票的收益形态以检验上述假设的合理性。
方差-协方差矩阵
上一章选择风险投资组合时,都是试图使组合风险最小化。确定投资组合的风险时,使用了三种资产的方差-协方差矩阵。作为对比,表VCV包括8种资产的收益数据(实际上是超额收益的对数),并计算了收益的相关系数矩阵和方差-协方差矩阵。计算可由用户定义函数CorrMatrix和VCVMatrix来实现。其中,CorrMatrix函数中包含了Excel的CORREL函数,VCVMatrix函数使用了Excel的COVAR函数。程序非常简洁,可以从表Module1中得到。如图所示,得到的方差-协方差矩阵中包含8个方差和28个不同的协方差。
VCV Matrix:VCV矩阵
【参照书中第130页的图】
图 工作表VCV中8种相关股票收益的方差-协方差矩阵
n种资产的VCV矩阵包括大约n2/2个不同的单元格。当存在许多可供选择的资产时,计算VCV矩阵的工作量相当大,预测时还会出现许多困难。夏普的单因素模型就把输入数据的个数从一个不可能的量级n2减少到可以实现的量级n。夏普假设除了各种资产与市场的一般联系之外,不同资产之间没有协方差(单因素)。因此该模型中没有考虑资产之间协方差,而资产之间协方差显著不为0的情况在这些资产所代表的公司同处一个行业时是很常见的。
使用单因素VCV方法时,需要知道每只股票的βi值和特有风险σ(ei)的估计值,以及市场指数方差σM2。方差-协方差矩阵的非对角线元素可以通过下面的公式估计:
对角线元素可以由单因素模型的风险分解方法得到:
为了用一个简单的公式得到矩阵,我们用HLOOKUP函数把β值和特有风险从输入数据中摘录出来,同时用一个IF语句为每一个对角线元素加上相应的残差方差。如图所示,单元格M41所包含的公式是:
第一项,最开始的HLOOKUP函数用匹配L列中股票名的方式找到βi,第二个HLOOKUP函数用匹配第40行股票名的方式找到βj。IF语句用来找到对角线元素(行和列的股票名相同),并且正确的加上残差方差。虽然公式很长,但是要注意使用IF语句时,最好只涉及公式中额外的元素,从而使公式容易理解一些。
Input Data for Single-Index Model VCV:
为单因素模型VCV输入数据
【参照书中第131页的图】
图 由单因素模型β值估计出来的方差-协方差矩阵
比较图和的数值之后,就会发现单因素VCV矩阵低估了原来的VCV中那些处于同一行业的股票之间的协方差。(那些资产依次代表两个银行、两个保险公司和三个化学公司。)
用户定义函数VCVSingleIndexMatrix只需将收益数据作为输入变量,就可以自动得出β、特有风险的估计值,并且给出单因素VCV矩阵。该函数从单元格M55开始。节给出了该函数的具体程序。
风险值(VaR)
本节我们介绍风险值(VaR)的概念,并在收益服从对数正态分布假设的条件下,得到股票风险值的解析解。
对股票收益的实证研究表明,它们通常具有一定的偏斜度。但是,对收益取对数后(通常是自然对数)得到的分布会更加对称。因此与收益相比,对数收益通常更加对称并且近似的服从正态分布。当对数收益服从正态分布时,称收益服从对数正态分布。虽然在某些情况下(例如处理日收益或资产收益波动性很低时)经常会忽略对数正态和正态分布之间的显著区别,但精确的研究中绝对不能忽略这种差别。我们至少要理解这两种分布之间的区别,这一点非常重要。两者的一个主要区别是:专家在他们的学术研究中一般使用对数收益,而在一般的商业软件中通常使用普通(未经处理的)收益,并且假设它们服从正态分布。另外,研究中倾向于用超额收益(收益减去无风险利率)作为分析的基础。本质上讲,关键要知道在分析中,需要假定股票价格收益服从何种分布。
严格地说,我们需要检验‘收益’数据以确保这些数据大致服从正态分布。一个比较简便的方法是生成一个正态概率分布图,例如Beta表中股票A的对数收益。生成正态概率分布图的方法在第3章(节)中已经介绍过了。工作簿中的表1给出了股票A对数收益的散点图。
在对数收益服从正态分布的假设下,定义一个投资组合分布的左尾(lower tail)值,资产的价值会以一定的概率低于该值。分布的左尾(lower tail)值就是风险值或VaR。这种度量的本质是把度量股票收益的波动率转化为求正态分布的百分位点。
知道了月度对数收益的均值和方差(分别用M和V表示)后,还需要知道服从对数正态分布收益数据的均值(用M1表示)。因为本节和下节都要用到收益分布的矩,所以在图中对它们进行了总结,其中对数正态矩M1和正态分布矩之间关系是M1=exp(M+)。
如果一个资产的月度对数收益服从正态分布,其均值和方差分别为M和V,那么时间段δt内其收益同样服从正态分布,这时的均值和方差分别为Mδt和Vδt。根据标准正态分布的有关理论,收益均值减去倍标准差的概率为5%,用公式表示就是:
即收益比上式数值低的概率为5%。类似的,收益低于均值减去倍标准差的概率为%。‘z值’(这里z=)确定了分布的左尾区域(这里是5%)。如果一种资产的初始价值是S,那么在δt个月之后其价值将有5%的概率低于:
这就是该资产的风险值。更严格的说此处得到的是绝对风险值。只考虑波动率(忽略预期收益SMδt)时得到的就是相对风险值。
风险值只是一个数字,用来表示一个资产或投资组合可能出现的损失,风险值给出了在给定的时间段(如一个月)和合适的概率(如5%)下资产的最大预期损失是多少。在当前的实际应用中,风险值被大多数主要的投资银行用来集中度量其持有的投资组合每天出现损失的风险。这些投资组合可能几乎包括所有的金融资产,例如期权、债券、期货和股票。此处的解释只针对由股票构成的投资组合,而没有涉及如何计算其他金融资产的风险值(往往比较复杂)。
下面给出了由8种瑞士股票构成的投资组合风险值的计算过程,股票的收益展示在表VCV中。如图所示,这里使用的是这8只股票的月度对数超额收益率。从C列到J列给出了各种资产的60组月度对数收益率,其均值和方差列在第8到第10行中。拿UBS来说,单元格W8给出了它的均值(用公式=AVERAGE(C13:C72)算出),单元格W10给出其方差(公式=VARP(C13:C72))。计算相对风险值的公式主要用到了UBS的资产价值(单元格W16)、UBS的方差(单元格W10)、所选择的时间段(单元格W15给出的一个月)和标准差的倍数即‘z值’(单元格W19)。相对风险值(单元格W21)的意义可以解释为:如果持有现在价值为1000的UBS的股票,在今后一个月的时间内,其价值有5%的概率减少或更多。在计算相对风险值的过程中,我们假设资产在整个期间内有一个预期收益,但是这个收益很小,可以忽略不计。(在计算每天的风险值时通常这么考虑)
绝对风险值中包含了资产的预期收益(从单元格W12的M1可以得到,此处UBS的月度收益为%),并且要从相对风险值中减去这个正数(因为此处持有该股票)。计算预期收益率(单元格W12中的M1)的过程中用到了对数正态和正态分布矩之间的关系公式,该公式将在节介绍。因此,在我们的例子中单元格W24给出的的绝对风险值比相对风险值要低。在上面给出的那些股票中,风险值最低的是Roche,因为它的方差最小。
Estimating VaR using Lognormal Distribution:
利用对数正态分布估计VaR
Estimating Portfolio VaR using Lognormal Distribution:
利用对数正态分布估计资产组合的VaR
【参照书中第133页的图】
图 在工作表VCV中计算8只瑞士股票的风险值
在知道单只股票的风险值和它们收益之间的相关系数矩阵之后,估计整个投资组合(此处假设组合中8种股票的权重相等)的风险值也就不难了。单元格W30中给出了整个投资组合的相对风险值是,比所有单只股票的风险值之和要小很多,这从另一个侧面证明了投资分散化可以减小投资风险。图给出了用于度量风险值的一些用户定义函数。
水平财富
本节中,我们将使用资产的对数收益及其均值M和方差V进行一系列分析。先给出对数正态和正态两种分布矩之间的关系,如下所示:
表Forecast中,在假设资产收益过去表现可以延续到未来的条件下,给出了如何利用上述关系来估计未来某个时间点(如T年)的财富值。在具体估计的过程中,假设历史收益(以1+r的形式表示)是独立同分布的,都服从对数正态分布。未来财富被定义为一系列连续收益的乘积,因此它也服从对数正态分布。图就是从表Forecast摘录出来的。
令现在的财富值W0为1,一年以后的财富值为W1。我们用收益所服从的对数正态分布的矩来得到Ln(W1)所服从的正态分布的有关参数,并以此预测未来的财富值。
Generating probabilistic forecasts(Ibbotson & Sinquefield):
生成概率预测值
【参照书中第134页的图】
图 在工作表Forecast中通过计算来预测未来的财富值
工作表的B列中给出了过去70年的一系列年度相关收益情况,其中第一个和第二个数值分别为和(单元格B6和B7)。从M1,M2,M和V之间的关系我们可以得到:
其中M,V的值分别为、(分别列在图的单元格E6、E7中)。在对数收益服从正态分布的假设下,不同的预期收益分布的参数列在F列中,其中预测收益的方差与时间段的长度为负相关关系。无限期的预测收益为%,而收益的几何均值为%。E列给出了对于不同时间段由正态分布的参数得到的预测收益,B列是用对数正态分布的前两阶矩得到预测收益。
对于正态分布,针对不同的时间段可以做出预测财富值的分布图。其置信水平(例如上95%水平)由下面公式给出的“z值”确定:
其中,财富值将有95%的概率低于倍的标准差。通常,图中单元格K11给出的‘z值’可以用Excel的NORMSINV(pctile)函数计算出来,其中单元格K8中给出了相应的百分比。
给定所要预测的时间段T年和合适的“z值”之后,我们就可以通过下式来预测未来财富值:
下面的图给出了过去70年的实际财富状况和对未来20年财富的预测值,财富分布的上95%,中间(50%)和下5%水平都展示在图中。
Forecast of Horizon Wealth: 水平财富的预测值
Wealth index:财富指数
【参照书中第135页的图】
图 对预测的水平财富值所作的解释
正态和对数正态分布矩之间的关系
图中表格的内容值得仔细阅读,因为在教科书中经常忽略收益和对数收益之间的区别,并且无论是在研究股票还是在研究期权等其他重要领域时,理解对数正态和正态分布参数之间的差别对我们的研究有很大帮助。
资产 收益 对数收益
分布 对数正态 正态
一阶矩
二阶矩
一阶矩计算
二阶矩计算
【参照书中第136页的图】
图 正态分布和对数正态分布矩之间的关系
可以用分布的中心矩来描述这个分布(见上表中的正态分布),也可以用原点矩来描述这个分布(见上表中的对数正态分布)。两种分布的一阶矩都是其均值(记做M1或M),二阶中心矩是方差V,二阶原点矩为M2。在Excel中,可以对所有收益使用SUMSQ函数,然后把计算结果除以收益的总个数,这就得到了M2。
由于正态和对数正态分布的矩存在上面给出的等价关系,可以根据一个分布的参数求出另一个分布的参数。假设对数收益服从均值为M、方差为V的正态分布。于是利用上表第二列最后两行给出的公式可以算出相应收益服从对数正态分布时的原点矩M1和M2。同样,知道收益服从对数正态分布时的原点矩之后,可以利用表中第三列最后两行的公式求出收益服从正态分布的均值和方差(M和V)。
Module1中的用户定义函数
本模块中的许多函数都能把工作表中的公式直接转化到VBA。这些函数包括ISHorizonWealth函数和那些函数名前面冠有Portfolio的函数。函数CorrMatrix和VCVMatrix分别用来计算相关系数矩阵和方差-协方差矩阵,它们只需要输入-收益矩阵(retsmat)。这两个函数都系统地选择了收益矩阵中的两列,应用Excel中的一些函数(这里主要使用了CORREL和COVAR)来计算度量指标,然后在输出矩阵中储存计算结果。
这里需要对VCVMatrix程序进行一点说明。在计算VCV矩阵时我们采用了样本度量函数(例如VAR函数),而不是总体度量函数(如VARP函数)。更为混乱的是,Excel中的COVAR函数是用来度量总体值的,其计算乘以n/(n-1)时才能得到样本值。
本模块中最具独创性的函数是计算单因素VCV矩阵函数。它有两个输入:收益矩阵(retsmat)和市场收益向量(mktvec)。下面详细给出了该函数的程序。注意:在输入矩阵的阶数确定之后,rvec()等变量的维数在后面的程序中要用ReDim语句加以声明。
Function VCVSingleIndexMatrix(retsmat, mktvec)
’returns nxn sample single-index variance-covariance matrix
’uses PortfolioBeta fn
’uses PortfolioSpecificRisk fn
Dim vmkt, bi, sri
Dim i As Integer, j As Integer, nc As Integer, nr As Integer
Dim rvec() As Variant, bvec() As Variant, srvec() As Variant, Vcvmat() As Variant
nc =
ReDim Vcvmat(nc, nc)
ReDim bvec(nc)
ReDim srvec(nc)
nr =
ReDim rvec(nr)
’first calculate the input (beta, specific risks and market variance)
For j = 1 To nc
For i = 1 To nr
rvec(i) = retsmat(i, j)
Next i
bvec(j) =PortfolioBeta(rvec, mktvec)
srvec(j) = PortfolioSpecificRisk(rvec, mktvec, 1)
Next j
vmkt = (mktvec)
’then cycle through vcv matrix
For I = 1 To nc
bi = bvec(i)
sri = srvec(i)
For j = 1 To nc
If j = i Then
Vcvmat(i, j) = (bi^ 2)*vmkt + (sri^ 2)
else
Vcvmat(I, j) = bi*bvec(j)*vmkt
Vcvmat(j, i) = Vcvmat(I, j)
End If
Next j
Next i
VCVSingleIndexMatrix = Vcvmat
End Function
在第一个If… Then循环中,j指第j种资产,i指第i个收益,用其他的用户定义函数估计了每种资产的β值和特有风险。第二个If… Then循环则在VCV矩阵的合适位置插入了基于β值和特有风险的方差-协方差表达式。
小结
单因素模型的主要贡献是把第六章中所研究的风险分为两个部分:第一部分是直接受指数或市场组合影响的风险,第二部分是单只股票或资产自身特有的风险。本章是应用单因素模型来估计方差-协方差矩阵,并且用回归分析得到了β系数。给出了分别使用工作表和用户定义函数时的所有计算过程(包括回归和矩阵乘法)。
虽然单因素模型和资本资产定价模型的数学原理极为相似,但是CAPM要比单因素模型向前多走了一大步。从CAPM的模型公式可以看出,只有投资者承担的市场风险才能得到回报。
可以通过单个投资者的效用和第六章介绍的常见组合问题推导出CAPM模型,同样可以采用股票的对数收益服从正态分布的假设,直接得到CAPM模型。在此正态假设的基础上,本章解释了如何根据历史收益预测投资者未来财富的分布和如何计算风险值。
下一章,将研究如何用单因素和多因素模型的被动标准来度量主动的投资战略。
参考文献
Bodie, Z., A. Kane and A. J. Marcus, 1996, Investments, 3rd Edition, Richard E. Irwin, Englewood Cliffs, NJ.
第8章 投资组合业绩评价
投资组合业绩评价的主要目的是估计和比较不同投资策略的业绩(历史收益)。对于由风险资产构成的投资组合,本章的内容是选择被动的还是主动的投资策略。另外,还将介绍在确定风险投资组合的条件下,如何选择无风险资产的投资水平。
对于一个完全被动的投资策略,投资者持有的组合完全复制市场指数(即成份股及其权重与市场指数完全相同)。被动的投资者并不需要过多信息,并且一般不交易其投资组合所包含的股票,除非市场指数的结构(成份股或者权重)发生变化促使投资者改变持有的投资组合。被动的投资者承受了市场风险并且获得相应回报,其收益等于市场收益减去必需的交易成本。
与之相反,主动投资者所持有的投资组合与市场指数构成不同,主要表现在投资组合的部分或是所有股票的权重与市场指数不同。主动投资者需要得到充分的信息并且比被动的投资者承担更多的交易成本。实际上,因为主动的投资者承受了特殊的风险并且承担较多的交易成本,所以从长期来看只有少数的主动投资者所获的收益超过了被动的投资者。随着主动的投资策略变得越来越复杂,所以急需发展更为适用的组合业绩评价方法和评价标准。
本章回顾了最早在二十世纪六十年代兴起的有关投资组合业绩评价的有关想法和九十年代提出的最新的投资组合业绩评价理论。所有的研究方法都用到了资产的收益(这样做的好处是股票和投资资金的相关数据都是可以得到的)。传统的投资组合业绩评价理论是与前面章节介绍的资产定价理论同时发展起来的。因此,夏普比率采用无风险资产的收益作为收益基准,而其他三种方法以CAPM(单因素模型)为基准。七十年代出现了另一种方法-特雷纳-布莱克模型(特雷纳和布莱克,1973),这种方法把那些定价不合理的股票和一个被动的市场指数投资组合混和起来,构筑了一个最优风险投资组合。这种方法是基于第六章中讨论的常见组合问题提出的。投资组合业绩评估和特雷纳-布莱克模型在Bodie et al(1996)著作的第二十四章有详细介绍。但是,Bodie et al的书中并没有介绍评估投资组合贡献的最新方法-风格分析(Style Analysis)。风格分析采用了多因素模型作为收益基准,它是夏普于1992年提出的。
四种传统的投资组合业绩评价方法包括用投资组合相对于某个收益基准的收益除以相应风险指标(如波动性、β值或特有风险)所得到的比率。表详细解释了这些评价方法。针对这些方法,也给出了相应的用户定义函数。类似的,用电子表格来实现特雷纳-布莱克“主动-被动”模型的过程中使用了一些函数,这些用户定义函数在我们解决常见组合问题时已经介绍过了。类似于求出有约束有效边界时的处理方法,风格分析也用到了Excel的规划求解来解决二次规划问题。风格分析还有两个扩展应用:求风格权重的置信区间(同样用规划求解来实现)和暴露分析(exposure analysis,即一个滚动时期的风格分析,它表示了随着时间的改变投资基金风格的变化情况)。因为进行暴露分析时反复用到了风格分析,所以我们将用Excel的宏来实现暴露分析。
传统业绩评价方法
传统的投资组合业绩评价方法主要通过比较不同投资策略的历史收益和风险对其进行评价的。因为这些方法仅以单因素模型作为基准,所以现在看来都有点过时了,并且它们都缺乏统计上的准确性和说服力。在此我们只把它们作为历史方法进行介绍。
表4Measures展示了二十世纪六十年代后期在CAPM基础上发展的投资组合业绩评价方法。它们的定义由如下公式给出:
夏普的方法:
特雷纳的方法:
詹森的方法:
Appraisal比率:
其中,rp和σ(rp)分别是投资组合的平均收益和收益标准差,αp和βp是CAPM模型中描述该投资组合和市场表现之间关系的参数,σ(ep)是投资组合收益的特有风险(即收益的标准差中不与市场相关的那部分),rf是无风险资产的收益。这些评价方法是基于历史可以反映未来的假设,用历史(后验)数据计算相应指标从而预测(先验)未来的情况。
夏普比率用超额收益除以投资组合的风险(投资组合收益的标准差)。特雷纳指标是用超额收益除以系统风险βp。詹森指标仅仅是αp,它等于投资组合的实际收益减去市场收益已知的情况下用CAPM预测出来的组合收益,相当于“定价偏差”。Appraisal比率等于αp除以投资组合的非系统风险。
Performance Measurement: 业绩评价方法
Excess Returns:超额收益
Four Performance Measurement: 四种业绩评价方法
【参照书中第140页的图】
图 工作表4Measures中的业绩评价指标
图中C和D列给出的收益均值,α,β等都是通过单只股票和市场指数的月度超额收益得到。(注意:我们同样可以选择一个投资组合的收益来代替上面提到的单只股票的收益)单元格C11中显示股票的α值(即单元格H12中的詹森指标)为正,这说明该股票的收益比用CAPM模型预测的结果要高。同样,单元格H14显示的Appraisal比率也是正的。特雷纳指标等于一个常数加上αp/βp,这个常数就是市场组合的超额收益。但是,在我们的例子中,α值不足以补偿该股票比市场组合大的多的总风险,因此,如果用夏普指标来评价的话,该股票的表现比市场要差。
上面几种评价指标分母上的风险指标是不同的,这在很大程度上决定了这几种方法的适用范围。例如,夏普指标只适用于评价整个投资组合的表现(因为它忽略了与市场的相关性)。特雷纳指标和詹森指标只适用于评价整个投资组合中的一部分投资业绩。Appraisal比率只适用于评价在一个核心的被动投资组合的基础上,进行的几个不同的主动投资策略的业绩。使用这些单因素的业绩评价指标时必须要小心。一般来说,这些方法比我们在本章后面部分介绍的多因素业绩评价方法(例如风格分析)要差。
主动—被动管理
实践中,许多投资经理假设大部分股票的价值是合理的,但是也有一部分股票被高估,另一部分被低估。根据CAPM模型,在一定程度上,可以用股票的α值来评价其价值被高估或低估的程度。
基于股票市场不是完全有效的假设,特雷纳和布莱克给出了一个如何评估由定价不合理股票构成的投资组合的模型。首先,他们给出了如何混和最优主动投资组合和被动市场组合来得到最优风险投资组合。其次,他们描述了如何在无风险资产和最优风险投资组合之间分配投资来构造最优投资组合。上面的两个步骤与前面我们解决常见组合问题时首先考虑两种风险资产、然后考虑一种风险资产和一种无风险资产的做法是一致的(即分别在和节中介绍的问题二和问题一)。特雷纳-布莱克方法采用了Appraisal比率,它是投资管理中常用的“core-satellite”方法的基础。
Active-Passive Exercise:主动-被动练习
A:Find Optimal Active Portfolio:A:发现最优主动投资组合
Optimal Active Portfolio: 最优主动投资组合
【参照书中第141页的图】
图 工作表AP中的特雷纳-布莱克模型
图的例子中,给出了投资经理认为定价不合理四只股票的一些具体指标。例如,股票1在下一年的预期超额收益为1%(超出用CAPM模型得到的预测值)。给定四种定价不合理股票的预期收益、β值和特殊风险后,可以首先得到最优主动投资组合。特雷纳-布莱克方法认为应当使主动投资组合的Appraisal比率最大化。并证明了上面这种方法的结果等价于持有这几种股票:各股票的权重等于其预期收益除以各自的特有风险。这些比率列在单元格G12:G15中,它们之和列在单元格G17中,最优主动投资组合中这四种股票的权重列在单元格I12:I15中。例如,最优主动投资组合中股票1的权重为%(=G12/G17)。结合这四种股票的权重以及其他信息,可以得到最优主动投资组合的预期超额收益为%,β值为,特有风险为%(假设四种股票的特定风险是相互独立的)。
接下来的任务是在最优主动投资组合和被动市场组合之间分配投资(对于后者,其超额收益等于单元格中的股权风险溢价,总风险等于单元格中的股票市场风险)。这里可以利用节中关于问题二的结论。节主要解决了存在两种风险资产的情况,这同样适用于存在两种投资组合的情况。因此图的单元格I23给出了主动投资组合的权重,其计算公式为:
B:Find Optimal Risky Portfolio:B:发现最优风险组合
Active:主动;Passive:被动;
Solve Prob2:Optimal Risky weights——解决问题二:最优风险权重
Optimal Risky Portfolio:最优风险组合
【参照书中第142页的图】
图 在工作表AP中求出最优风险投资组合
这个公式的输入项主要有:这两个投资组合的收益和风险(B24,E24,B29和E29)、它们的相关系数(B31),无风险利率(B4),风险厌恶系数(B7)。结果显示有%的资金是投资于主动投资组合的。注意到最优风险投资组合的夏普比率比被动投资组合的要大,这说明最优风险投资组合为风险提供了更多回报。
最后的任务是解决如何在最优风险投资组合和无风险资产之间分配投资。同样,这个问题的解决方法已经在节中介绍过了,是第一个常见组合问题。图的单元格B41给出了最优风险投资组合的权重,其公式为:
其中,此公式的输入项为:最优风险投资组合的收益和风险(I28,I32),无风险利率(B4),投资者的风险厌恶系数(B7)。计算结果显示,投资于风险资产的比例少于2/3(%)。
C:Find Optimal Portfolio:C:发现最优投资组合
Solve Prob1:split between risk-free and risky
解决问题1:在风险资产与无风险资产之间分配投资
Optimal Portfolio:最优组合
【参照书中第143页的图】
图 在工作表AP中确定特雷纳-布莱克模型中风险资产和无风险资产的权重
图中的风险-收益图解释了主动和被动投资组合。根据资金在主动和被动投资组合之间分配的不同,可以得到二者之间的边界(边界上的每个点反映了两者的一种组合)。当过图上代表无风险资产(收益为7%)的直线与该边界相切时,就得到了最优风险投资组合。风险厌恶系数不同将会改变最后的最优投资组合在直线上的位置。本例中无风险资产的投资比例为%。
Active-Passive Exercise:主动-被动练习
Portfolio return:投资组合收益
Portfolio risk:投资组合风险
Risk-free:无风险
Passive:被动
Active:主动
【参照书中第143页的图】
图 工作簿EQUITY3中特雷纳-布莱克模型的图表分析
风格分析(Style Analysis)
风格分析(style analysis)是最近发展起来的,基于收益的,投资基金评价方法。夏普(1992)在九十年代初期率先建立了一个“资产类因素模型”,并根据风格(Style)和选择(Selection)来评价不同基金的业绩。风格分析可以看作得到组合的逆过程。
投资基金可以分配到许多不同的国内市场(例如股票、债券和票据)和国外市场(例如货币、外国股票和商品)。其中的每个市场还可以包括许多不同种类的资产。对于局外投资者而言,通常不能得到一个特定基金所投资资产的详细信息。因为不同市场资产的业绩是不同的,所以,一方面很难分辨出每个市场对整个投资组合的收益所作贡献的大小,另一方面也很难分辨出每个市场中不同资产的贡献大小。但是,可以得到该基金的收益数据,从而可以进行风格分析。
风格分析是用一些已知的指数(其收益是可以得到)构建基准投资组合,然后将投资基金的主动投资组合的业绩与该基准组合进行比较。理论上,这些指数应该能反映不同资产类别的活动性,这些指数应该是唯一和完全的,并且所包含资产报价是公开的,这样一来,我们就可以“被动地”追踪这些指数。(例如,夏普选择了12个能包括美国投资基金投资选择的指数,并保证这些指数之间的重叠部分尽量少。)
用f1、f2…fn表示n种被动指数的收益,则用这n种指数对第i种投资基金进行风格分析所用的公式为:
其中ri是第i种基金的收益,bij是第i种基金在第j种指数中的权重,ei是收益中不能用上述因子解释的部分。
把上面的收益多因素模型改变一下形式,转化为基金收益和指数收益之差:
其中括号中的部分可以看作是一个投资组合的收益。
夏普认为应该选择合适的权重bij使得“追踪误差”ei最小化,或者使ei的方差(它是权重的二次函数)最小化。因为这些指数构成了一个投资组合,所以相应的权重之和应该为1,并且每个指数的权重应该在0-1之间。(对于基金来说,后面的那个约束条件可以修改为允许卖空资产。)最优化之后得到的权重被称为风格权重,并且与其相应的指数共同构成基准投资组合。我们称由各种指数按照相应最优风格权重构成的基金与原有基金的风格是相同的。
可以用二次规划来最小化样本期内的追踪误差,并得到相应的风格权重。利用Excel中的规划求解过程(选择“工具”,然后点击“规划求解”)可以在工作表中很容易实现上述计算。
可以通过计算基金收益的方差中由所选择的风格模型解释的比例大小来判断风格分析的效果。这与回归分析中经常用到的决定系数R2类似,对于第i种基金的计算公式如下:
夏普用同样的一组指数,针对不同的投资基金构造了不同的基准投资组合,并应用上述方法对这些基金的风格进行比较。下面几节中我们将针对一个基金,研究它的风格和特征。接着,我们将研究风格权重是如何随着时间改变而改变的,这同时也反映了该基金中各种资产头寸的变化。
简单风格分析
EUQUITY3中的Style1表包括一个基金和8个被动指数的月度收益数据。图给出了该工作表最上面部分的内容。现在需要求出使误差方差(单元格M6)最小的权重(单元格D16:J16中)。这里用Excel的规划求解过程来实现。注意:C16中的公式是1-SUM(D16:J16),这保证了所有的风格权重之和为1。
进行分析前,应该先给出风格(单元格L21:L80)和追踪误差(单元格M21:M80)的有关公式。第一个月的风格计算公式(单元格L21)为:
Style Analysis :风格分析
Solver-Style Model:利用规划求解进行风格分析的模型
【参照书中第145页的图】
图 工作表Style1中的简单风格分析
公式中用SUMPRODUCT函数把风格权重和相应的指数收益相乘。第一个误差项(L21)就是单元格B21中的基金收益和风格收益之差。同样我们可以应用上面的公式计算出其他月份的风格和误差,这时单元格M6中的误差方差可以用样本方差函数计算出来:
因为误差方差值一般很小(这里是),为了提高优化的精确性,在此基础之上加上了规模误差方差值(乘以10000)(如单元格M5所示)。
第15和17行给出了权重的最小、最大值等约束条件,在此约束基础上我们可以用规划求解来最小化单元格M5中的规模误差方差。图的最上面对规划求解和得到的风格权重进行了一些详细说明。上述最优化过程可以通过菜单操作和编程来实现。菜单操作就是使用工具中的规划求解;编程则要通过子程序直接调用规划求解中大量不同种类的函数。第节将简要介绍了相应的、在ModuleM中名称为Style1的相应宏。同时按下组合键Ctrl+Shift+S可以调用该宏。
宏Style1还可以生成一个表,其中包括对于选定的指数使基金收益的误差方差最小的风格权重。从表中可以看出,在所研究的期间内,该基金的业绩相当于这样一个投资组合:index3的权重为%,index5的权重为%,index4的权重为%…。此例中选择的风格可以解释基金收益%的波动性。总之,根据一个特定基金的风格权重可以构造一个基准投资组合,在所研究的期间内该组合收益等于基金收益,根据该基准组合就可以比较不同的主动基金经理的投资管理水平了。
滚动时段风格分析
前面一节中定义的风格实际上是整个研究期间内的风格平均值,要注意到风格是随时间不断变化的。另一种分析方法是对于一系列连续的时期进行一系列的风格分析,这样我们可以看出随着时间的变化,风格是否一致。这种滚动时段的风格分析有时被称为暴露(Exposure)分析。
如图所示,我们分别对前24个月、第7-30月等时间段的收益进行了风格分析,得到了7个风格分析的时序数据(每次分析与前面的分析都有18个观测是相同的)。
跟上一节相比,使用电子表格计算时需要格外注意的是:计算每次风格分析的误差方差时要正确选择相应的误差项(如计算前24个月的误差方差时要选择相应的1-24月的误差值,而不要选择2-25月等)。为了做到这一点,可以用Excel中建议的格式使用INDEX命令,从而为方差计算公式选择正确的开始和结束单元格。单元格M23给出了每次风格分析中计算误差方差的公式,其中开始月份列在J6,结束月份列在J7中:
如果J6中的数值为1,J7的数值为24,那么上面公式等价于公式VAR(M38:M61)。
Rolling Period Style Analysis-Exposure:滚动时段风格分析-暴露分析
Solver-Exposure Model:利用规划求解进行暴露分析的模型
【参照书中第147页的图】
图 工作表Style2中的暴露模型分析
最好使用宏来自动实现重复的风格分析。在ModuleM表的Style2宏里,首先确定合适的目标单元格、可变单元格和约束条件来建立规划求解。在重复执行规划求解的过程中,正确选择误差方差的起止范围(第几月开始到第几月结束),用规划求解来最小化误差方差,并且把求出的权重复制到输出区域。图给出了风格分析权重的时序数据图。同时按下Ctrl+Shift+E这个组合键可以调用该宏。
Exposure Analysis:暴露分析
【参照书中第147页的图】
图 在滚动时段分析中改变风格权重
我们可以用暴露权重(从过去的24个月的数据估计得到)构造一个基准投资组合,并比较该组合和主动基金在当月的业绩。月末就可以比较主动基金和它的暴露基准组合的收益情况,两者业绩的差异被称为当月的选择收益(Selection Return)。这个选择收益度量了在该风格部门里选择的股票的影响和当月对基金所作的动态调整引起不同部门相应权重变化的影响。例如,一个基金中某部门业绩较好的股票的比重较大时将为其带来正面影响,而增加业绩不好部门的头寸将为其带来负面影响。对一些月份的数据进行上述分析,就可以得到该主动基金的累积选择收益。夏普给出了如何用t-检验来检验平均选择收益的显著性。
风格权重的置信区间
到现在为止,已经估计了风格权重(分别在简单和滚动的基础上),但是仍然不知道这些权重的估计值是否显著不为0。为了使用统计方法来检验这一点,需要计算风格权重估计值的标准差。
理论状态下,风格分析中使用的指数应该是相互独立的,因此任意指数的收益与其他指数的收益不相关。实际上,指数一般包括多种可用资产,因此有时候与其他指数相关性较高,不满足理论假设。现在需要做的是从风格分析中去掉那些与其他指数过于相似的指数(替代),这样余下指数的相关性就大大降低了。例如,开始分析的时候可能有8种指数,而经过逐步筛选将剔除4种相关性较强的指数,最后可能利用互补性最强的4种指数进行风格分析。
判断指数之间替代性或互补性的一种方法是计算不同指数收益的相关系数矩阵。那些容易被其他指数复制的指数与其他指数的相关性很强。如图所示,指数3,6和8与其他四种指数的相关系数都在以上。与之相反,那些很难复制的指数与其他指数的相关性较差。例如,指数1和2与其他指数的相关系数都在以下。
Correlation Matrix for Style Index Returns:风格指数收益的相关系数矩阵
【参照书中第148页的图】
图 风格指数收益的相关系数矩阵
风格分析是在有约束的线性回归基础上发展起来的,因此,应该可以找到风格权重的置信区间。这可以通过用其他的指数来估计该指数的风格来实现(Lobosco和DiBartolomeo于1997首先指出了这一点)。上述风格的估计过程中没有权重进行约束(除权重之和必须为1之外),因此对于每个指数都会带来一个无法解释的波动率。这与前面介绍的评估基金业绩的风格模型有较大差别:那个模型中,为了得到所估计的风格权重的标准差(和置信区间),首先给出了模型的主动标准差。这些都展示在图和中。
Confidence Intervals for Style Weights
风格权重的置信区间
Solver-Style Model for Confidence Intervals:
利用规划求解进行风格置信区间分析的模型
【参照书中第149页的图】
图 工作表Style3给出的风格系数的置信区间
需要这样一张电子表格,它把指数收益矩阵自动分为两部分:B列是选定的单个指数的收益j*;D到J列是余下那些指数的收益。用单元格B10中的j*值和INDEX命令把选定的单个指数分离出来。这里用数组的形式来返回收益数组中选定单元格的数值。上面的这些步骤用公式表示在B21中就是:
为了更方便的分开余下的那些指数,可以设计一个名为StyleSubMatrix的用户定义函数,表Module1中介绍了该函数的一些具体的情况。
虽然现在对于单个指数的权重没有约束条件,但是还需要仿照表Style1的方式创建新表并用规划求解进行计算。这时使用宏Style3分析单个指数(用组合键Ctrl+Shift+J调用)、在集中分析8个风格指数时使用宏Style4(用组合键Ctrl+Shift+K调用)。表ModuleM详细解释了上面这些公式的有关情况。
Confidence Intervals for Style Weights
风格权重的置信区间
【参照书中第150页的图】
图工作表Style3给出的风格系数的置信区间
利用对每个指数分别求得的主动标准差,估计最初对该基金进行风格分析时得到的风格权重的标准差。单元格AB16中的标准差公式为:
ASD指数(单元格AB14)是标准差的除数。如果一个指数的ASD值比较高,那么它就很难用其他指数复制出来,标准差也比较低。这可以通过用权重的估计值除以合适的ASD指数实现。公式的计算结果也与最初进行风格分析时的样本观测值的数量和不为0的风格权重的个数有关。
应用t-统计量可以从风格分析中剔除那些t值显著为0的指数,使风格分析更为准确(在95%的置信度下低于2)。
风格分析是一种非常有用的分析方法。自从二十世纪六十年代发展了CAPM相关基本理论(和相关传统业绩评价方法)之后,学者们对如何发现在资产定价市场上出现的反常情况做了一系列的研究。根据研究结果,投资组合业绩的差异(一些时期业绩好,另一些事情业绩差)是因为在不同的时期采取了大量的主动投资策略。到了八十年代,人们又发现了小公司效应和基于股利收益和市盈率(Price-Earning Ratio)的投资策略。九十年代的主要发现就是市净率(Market-To-Book Ratio)和动量分析(Momentum)。人们为了发现市场上可能的反常情况做出了很多努力,其直接结果就是发展了从被动基准投资组合(尤其是在美国和英国)到模拟主动投资策略的一系列理论。目前风格分析是这些理论中最重要的一种,因为它给出了一个多因素被动基准投资组合,在此基础上我们可以比较和评价现有的反常的主动投资策略的业绩。
Module1中的用户定义函数
本模块包括四种业绩评价方法所需要的函数。本模块仅仅把电子表格中的函数直接转化为程序。
其中有一个函数的作用是从收益矩阵中删除一行并得到维数减少的矩阵,这样我们就可以计算风格系数的置信区间了。本质上,该函数是对原有收益矩阵一行一行的完全复制,跳过需要删除的那一行(用虚拟变量jadj):
Function StyleSubMatrix(indxmat, jstar)
’returns style index returns matrix less column j*
Dim i As Integer, j As Integer, jadj As Integer, nr As Integer, nc As Integer
Dim Submat() As Variant
nr =
nc =
ReDim Submat(nr, nc - 1)
jadj = 0
For j = 1 To nc – 1
If j >= jstar Then jadj = 1
For i = 1 To nr
Submat(i, j) = indxmat(i, j + jadj)
Next i
Next j
StyleSubMatrix = Submat
End Function
中的宏
风格分析的二次规划公式比求有效组合的二次规划公式要略微简单一些。这两个公式为权重设定了上下限,但是风格分析中没有等价于满足目标收益要求的等式约束。Style1的子程序主要是规划求解的一个简单应用,它直接使用了规划求解中的函数。SolverAdd函数给出了约束条件,而用SolverOk解决:
Sub Style1()
Range(“change1”).Value =
Range(“A1”).Select
SolverReset
Call SolverAdd(Range(“constraint1”), 3, Range(“con1min”))
Call SolverAdd(Range(“constraint1”), 1, Range(“con1max”))
Call SolverOk(Range(“target1”), 2, 0, Range(“change1”))
Call SolverSolve(True)
SolverFinish
Call Chart1
= True
End Sub
Chart1子程序是用宏录制器编写的,它主要用图表向导在一张新的工作表上作图。程序最后两行的作用是用数据标签的形式显示数值并且减少图表的背景色彩。这些程序都是用宏录制器编写的,这要比边看帮助和使用说明边写程序简便得多。注意:这段程序可以单独使用,也可以在Style1中作为子程序调用。
Sub Chart1()
Source:=Sheets(“Style1”).Range(“chart1s”),_
Gallery:=xlColumn, Format:=6, PlotBy:=xlRows,_
CategoryLabels:=1, SeriesLabels:=0,HasLegend:=2, Title:=_
“Style Analysis”, CategoryTitle:= “”, ValueTitle:= “”, ExtraTitle_
:= “”
Type:=xlShowValue, LegendKey:=False
= xlNone
End Sub
子程序Style2仅仅是规划求解函数的重复应用。应该注意在初始化之后,规划求解是如何在循环之前建立起来的。每次循环将调用SolverSolve进行下一次风格分析,然后将求得的权重复制到输出区域。这里注意语句Range(“exp0”).Offset(iter, 1)中的Offset是如何在输出区域内循环使用的。
Sub Style2()
Dim iter As Integer, niter As Integer, rstep As Integer
Range(“exp17”).ClearContents
Range(“change2”).Value =
Range(“A1”).Select
SolverReset
Call SolverAdd(Range(“constraint2”), 3, Range(“con2min”))
Call SolverAdd(Range(“constraint2”), 1, Range(“con2max”))
Call SolverOk(Range(“target2”), 2, 0, Range(“change2”))
rstep = 6
niter = 7
iter = 1
Do While iter <= niter
Range(“smonth2”).Value = 1 + (iter - 1) * rstep
Call SolverSolve(True)
SolverFinish
Range(“constraint2”).Copy
Range(“exp0”).Offset(iter, 1).PasteSpecial Paste:=xlValues
iter = iter + 1
Loop
Call Chart2
= True
End Sub
接下来的子程序Style3和Style4的编写方法,与上面介绍的两段子程序类似。
小结
本章从两个不同视角研究了投资组合业绩的评价方法。第一视角的研究方法不是很细致,主要是在前面一章的理论基础之上发展的传统业绩评价方法。第二视角则根据多因素模型进行风格分析,并给出更为准确的判断标准。
传统的评价方法是根据投资组合收益和风险的历史数据对组合进行简单排序。这些方法很容易用电子表格实现,但是它们是由单因素(CAPM)模型发展出来的,也缺乏统计上的说服力和准确性。
基于部分股票的价格是不合理的假设基础之上,特雷纳和布莱克根据CAPM模型给出了“主动-被动”模型。该模型主要的任务是将主动投资组合与市场组合结合起来,构建一个最优风险投资组合。可以看到,第6章中提到的常见组合问题可以用来解决在“主动-被动”模型中遇到的问题。
在建立CAPM模型后的几十年里,学者们为了找到可以反映风险回报的、不同于β值的其他评价指标做了不计其数的实证研究。为了发现市场上潜在的定价偏差(如规模、市净率),又发展出来一些更为专业的指标(如大盘股、小盘股、价值和成长)。这些研究成果推动了多因素模型的发展,进一步为建立更好的投资组合业绩评价基准打下基础。
本章介绍了风格分析的方法,它是一种以多因素模型为基础的主动投资策略的评价方法。这种方法需要用二次规划来解决问题(就像在第6章那样),具体的实现过程可以使用Excel工作表中的规划求解或者是宏。可以对一段时期进行风格分析,但更为有用的是对一系列连续的流动时期进行风格分析,这样可以发现风格模式(Style Pattern)随时间的变动情况。
与传统的评价方法不同,利用风格分析可以进行统计推断。在对标准风格分析进行修改的基础上,可以给出风格权重的置信区间。同样可以检验一个主动基金随时间变化的Selection Return在统计上的显著性。
从二十世纪五、六十年代创建的投资组合最优化和资产定价理论到九十年代的风格分析,在这些理论的发展过程中,夏普做出了很大贡献-他发展了CAPM模型和风格分析。正如我们在附注的电子表格中看到的那样,这是理论和实际应用的一次完美结合,帮助我们更全面认识了股票。
参考文献
Bodie, Z., A. Kane and A. J. Marcus, 1996, Investments, 3rd Edition, Richard E. Irwin, Englewood Cliffs, NJ.
Lobosco, A. and D. DiBartolomeo, 1997, “Approximating the Confidence Intervals For Sharpe Style Weights”, Financial Analysts Journal, July/Aug, 80-85.
Sharpe, W. F., 1992. “Asset Allocation: Management Style and Performance Measurement”, Journal of Portfolio Management, Winter, 7-19.
Treynor, J. and F. Black, 1973, “How To Use Security Analysis To Improve Portfolio Selection”. Journal of Business, 46, 66-86.
第9章 股票期权介绍
期权的重要性是不容置疑的。自从1973年,布莱克和迈伦。舒尔斯出版了最早关于期权定价的论文以来,大量的金融产品被开发出来并在全世界交易。而且,除了股票,该理论还延伸到债券以及实物资产等领域。
看涨期权(Call Option)是一种能够以预先协定的执行价买入某种资产的权利;看跌期权(Put Option)则是一种能够以预先协定的执行价出售某种资产的权利。欧式期权(European option)只允许在到期日执行,而美式期权(American option)则允许在到期日之前(包括到期日)的任意时刻执行。尽管布莱克-舒尔斯定价模型是用来对欧式期权定价的,但实际交易中大多数是美式期权。
本书期权部分的结构有别于股票部分。本章总体介绍了期权的概念和理论,这些内容将在下面四章中作详细介绍。研究期权定价的目的是为各种衍生工具定价,下面的章节将介绍各种不同的定价方法。这些方法的适用性部分依赖于期权的类型,以及布莱克-舒尔斯定价模型及其扩展模型是否适用。如果不适用,可以使用其他的数值方法。本章介绍期权定价的一些主要思想,在后续四章的电子表格模型中会逐步充实这些内容。有些概念是环环相扣的,很多地方都需要用到它们,因此在本章提前介绍,然后在以后的章节扩展。
不同定价方法将在工作簿OPTION1,2,3,4的工作表中演示。一些小例子用来展示计算过程,用户定义函数中也包含这些计算过程。由于可以通过设置反映问题大小的参数(如二叉树定价的期数,模拟的次数等)来推广函数的使用,所以这种方式功能强大而且能自动处理大型计算。
本章首先介绍期权定价的发展历史,然后引入布莱克-舒尔斯定价公式,接下来介绍有关对冲投资组合(hedge portfolios)以及风险中性定价(risk-neutral valuation)的主要思想。期权的有些性质本章先不作介绍,例如欧式、美式期权的区别,波动率估计以及看涨期权和看跌期权的关系等等。最后讨论期权定价的数值方法。主要的定价方法包括二叉树(binomial tree)的运用(第十章)及其模拟(simulation)(第十二章)。第十一章重点介绍布莱克-舒尔斯定价公式,第十三章讨论某些假设条件(如对数收益的正态性)不成立情况下的定价方法。
象讨论股票那样,本书强调的是理解并在电子表格中实现有关期权的计算。本章并不介绍期权的所有背景理论,对于有关细节问题,建议读者查看相关的专业书籍(如赫尔,2000及Bodie et )。例如,简单期权的基本定义,它们的盈亏状况图,以及看涨和看跌期权的平价关系等在赫尔的第1和第7章,及Bodie et al的第19章中有介绍。期权定价的基本理论在Bodie et al的第20章有介绍,更深层次的内容可以在赫尔的第9,10和11章中找到。
布莱克-舒尔斯公式的起源
费希尔·布莱克(1989)曾进写过一篇短文介绍他和迈伦。舒尔斯怎样得出著名的布莱克-舒尔斯公式,为了强调其对现代金融学的杰出贡献,这里重点介绍其中的一些观点。在他们的分析中,核心思想是可以用一定数量的股票和一份期权构造一个完全对冲的组合。术语“对冲(hedge)”的含义是,由于期权价值的变动可以抵消股票价格的变动,因此组合资产的价值在一段较短时间内不随股票价格的变化而变化。因此,对冲投资组合也是一种无风险组合。
接下来,在1965年发表的论文中提出了资产定价模型(CAPM),许多研究机构设法将它运用于股票以外的其他领域,如债券,企业现金流,以及认股权证(warrant)等。当时(1965)的认股权证市场(由企业发行的一种长期期权)发展迅速,远甚于OTC市场短期期权。
学者们首先估计认股权证在到期日的预期收益,然后将其折现来得到其价格,这种方法忽略了股票的预期收益以及合理的折现率。布莱克发现认股权证的价值由股票的总风险决定而不是由股票的预期收益决定。
布莱克和舒尔斯发现,股票的预期收益等于确定状况下的无风险利率,因此,期权的预期收益也可以用这个利率折现得到。他们的手稿(1970年)提出了著名的布莱克-舒尔斯公式,但当时却被《政治经济杂志(Journal of Political Economy)》拒绝!在默顿·米勒和吉恩·法马的帮助下,文章才在1971年8月被接收,但要求作进一步的修改。文章最终发表在《政治经济杂志》1973年5/6月期刊上。同样在1973年,这不仅是巧合,芝加哥期权交易所(Chicago Board Option Exchange)成立,交易16家公司的股票看涨期权。
现在看来,布莱克-舒尔斯公式其实并不复杂,但是它在期权定价理论方面得到广泛应用,而且迈伦·舒尔斯和罗伯特·默顿也因此在1997年10月获得诺贝尔经济学奖,这些足以说明它的重要性。可惜布莱克在1996年去世,因此未能享受这一殊荣。
简要介绍完布莱克-舒尔斯公式的历史后,接下来看一看实际的布莱克-舒尔斯公式。
布莱克-舒尔斯公式
布莱克-舒尔斯公式可以直接给欧式期权定价,但它也可以为其他一些期权定价(欧式期权的特点是仅能在到期日执行)。股票部分已经提到过,假定期权标的股票对数收益服从正态分布。假设期权(标的股票的即期价格为S)是一个看涨期权,期限为T,只有在到期日才能执行,执行价格为X。那么在T时刻,期权的收益为:
是标的股票在T时刻的价格,是一个服从某种概率分布的随机变量。
通常可以将股票价格的运动看成是一个随机过程,或者更准确的说,是几何布朗运动(这种股票价格运动模型可以参看赫尔的第10章)。使用数学语言,这意味着随机变量可以写成随机的形式:
这里,随机变量服从正态分布,而变量则服从标准正态分布,即其均值为0,标准差为1。的均值为,标准差为,并且有:
,
其中是股票的期望收益,是股票的年波动率。由于等于,则同样服从正态分布,即股票的对数收益服从正态分布。
在布莱克-舒尔斯分析方法中,看涨期权可以与一定数量的标的股票构成完全对冲的无风险组合。因此这个组合的收益必须等于无风险收益。用一个偏微分方程(应用数学中常见的热扩散方程)表示。该方程的解就是布莱克-舒尔斯公式。
欧式看涨期权(不支付红利)的布莱克-舒尔斯定价公式为:
其中,为股票的即期价格,为期权在时刻的执行价,r为复利计算的无风险利率,因此表达式为时段的无风险折现因子,为的累积标准正态分布函数。这里,和可以表示为:
我们将在第11章更加深入的分析布莱克-舒尔斯公式。注意,股票的期望收益并没有出现在公式中。在我们完全理解对冲组合的重要性后,就会明白其中的原因了。
对冲投资组合(Hedge Portfolios)
在布莱克-舒尔斯公式中一个令人感到惊讶的地方是,股票的期望收益没有出现在表达式中。然而,这可由下面来解释,可以创建对冲投资组合,且公式中使用无风险利率。但是,术语“对冲投资组合”的确切含义是什么呢?
布莱克-舒尔斯看涨期权公式可以写成下面的形式:
,其中 ,
等式右边的投资组合,包括一定数量的股票和无风险贷款,它可以完全复制左边的看涨期权(该变量即“复制组合(replicating-portfolio)”)。 布莱克-舒尔斯公式显示,看涨期权的价值必须等于复制组合的净投资值。
将布莱克-舒尔斯公式变型为,就可以用看涨期权来构造一个对冲组合。买入h数量的股票,卖出一份看涨期权,即可构造出一个固定价值为B的对冲组合。我们用h来代替,由此可以看出,对冲比率实际上是用来对冲期权价格变动风险的股票数量。
假设在某一时段,股票价格的变动只是简单地以一个确定的比率上涨或下跌,即简单的单期二叉树过程。例如:假设在t=0时刻,股票的即期价格为100(S),在t=1时刻,股价如果上涨,则为115(上涨比率为);股价如果下跌,则为95(下跌比率为)。如果期权在t=1时刻的执行价格为100,那么在到期日,期权的收益要么为15(股价上涨),要么为0(股价下跌),见图。
注意,目前并不知道股票上涨或下跌的概率。但是,如果买入份的股票,而卖出1份看涨期权,这个对冲组合将得到一个确定的收益(上涨时为×115-15;下跌时为×)。由于组合的价值不依赖于股票的最终价格,即它是无风险的,因此该组合在这个时段必须获得无风险收益,如5%。所以将t=1时刻的收益折现到t=0时刻的折现因子应为。使净投资表达式(*100-c)与折现收益()相等,可以求得期权的价格c(等于)。
通过分析布莱克-舒尔斯公式,可以得到构造对冲组合的合理对冲比率(h=)以及期权的价格(c=)。
【参照书中第160页的图】
图 股票价格变动的单期二叉树模型
归纳一下图中的例子以及图中的符号说明,可以得出,如果,即,则投资组合在t=1时刻的收益就是确定的。
因此,对冲比率为测量期权价格变动相对股票价格变动的比率。
【参照书中第161页的图】
图 股票、看涨期权及对冲组合在单期二叉树模型中的符号表示
风险中性定价
对冲组合的存在使得我们可以运用风险中性定价方法来计算期权价格。在风险中性的世界里,所有的投资者都对风险无所谓,因此所有的资产(并不限于布莱克-舒尔斯分析中的对冲组合)可以通过对其预期收益折现来定价。注意,这里并没有说所有资产的风险都是相同的,或者真的无风险,只是说在采用这种定价方法时可以假设投资者是风险中性的。
在风险中性的世界里,期权的价格等于其预期收益按无风险利率折现的现值,即:
其中,指在风险中性概率测度Q下的期望收益。变换一下布莱克-舒尔斯公式,就能够更加清楚地看出这一点:
上面方括号中的表达式就代表期权的期望收益。则表示期权在风险中性条件下被执行的概率。
在接下来的章节中,将会不断出现有关风险中性条件下的期权价格计算。例如,在第10章,二叉树将会提供一种计算期望收益的结构,而在第12章,蒙特卡罗模拟和数值积分技术都提供了计算期望收益的代替方法。
风险中性定价的单期二叉树模型
前面已经讨论了在风险中性条件下的定价问题,当时只简单地假设股票的变动是单期二叉树过程。在风险中性条件下,所有的资产都有相同的期望收益,因此股票和债券的期望收益相同。然而,债券可以带来确定的固定收益,比如说5%,而与股票的收益无关。假设股价上涨时股票的收益为15%,而股价下跌时收益为-5%。接下来就是要找到股价上涨和下跌的隐含概率,使得股票的期望收益与债券相等。
资产的期望收益是其各种可能收益的加权平均,记股价上涨的概率为p,则股票的期望收益为:
结果取决于p值的大小。如果假设p=,则股票的期望收益为,正好与债券相同。我们称此时的p值()为风险中性下的隐含上涨概率。
结合图中的符号,如果股票在t=0时刻的价格为S,而在t=1时刻,股价上涨时为uS,概率为p;股价下跌时为dS,概率为1-p,则股票在t=1时刻的期望收益为:
其中u和d分别为股票价格的相对变化量。如果单期的无风险利率为r,则根据风险中性定价,可以得到:
也就是
(严格地说,对于时间段,折现因子应该是,而不是简单的,因此,隐含概率为。)
得到了隐含概率(implied probabilities)后,就可以利用它们求其他资产进行风险中性定价。看涨期权的价格其实就是期权加权平均收益的现值(或折现值)。如果期权的执行价格为100,则股价为115时收益为15,而股价为95时收益为0。因此,该期权的期望收益为×15=。用无风险折现因子对其折现(使用前面提到的单期折现因子),可以得到期权的价格为,这与前面用对冲组合得到的结果一致。
风险中性定价方法适用于所有资产,而不仅仅是无风险资产。相应的股票现值为[(115)×+(95)×]=。
很明显,多期二叉树是对单期二叉树方法的改进,它会给出一个更精确的股票终值(),也更加接近股票价格的分布。例如,如果期权的有效期是三个月,那么一个两期二叉树将有三个不同终值,一个九期二叉树将提供十个不同终值,即n期二叉树将提供(n+1)个不同终值。对于一个九期二叉树,每期代表1/3个月。我们需要知道二叉树每期时段的长短,股票价格向上(或向下)运动的概率,以及价格在经过九期变化后的累积效果是否与我们假设的股价分布模型一致。终值服从二项概率分布,当期数增加,离散的二项分布将逐步接近连续的正态分布。增加期数的目的是为了用二叉树模拟前面提到的随机过程,也就是假设股票价格服从的几何布朗运动。
通过对某一时段股价变化建模,可以用一个多期二叉树来模拟布莱克-舒尔斯方法中的正态分布。由于该方法广泛地用于对各种期权定价,因此二叉树定价的基本方法将在下一章做深入分析。
期权平价关系(Put-Call Parity)
前面的布莱克-舒尔斯公式计算的是看涨期权价格。和看涨期权一样,也有看跌期权,它提供一种在到期日能够以执行价X出售资产的权利。在经过时段T后看跌期权的收益为:
欧式看涨期权和看跌期权(仅能够在到期日执行)之间存在一种非常有名的关系,被称为看涨期权和看跌期权的平价关系,如果看涨期权和看跌期权的标的股票(当前价格为S)相同,且执行价也相同,则有关系式:
其中, p是看跌期权的价格。右边看跌期权和股票的组合完全复制了一看涨期权和负债的组合。这意味着看涨期权的定价公式很容易应用于看跌期权。因此,布莱克-舒尔斯关于看跌期权的定价公式为(标的股票没有红利):
注意,看跌期权的对冲比率等于,为负值,而看涨期权的对冲比率是,为正值。
一般不推荐用两个不同的公式来计算看涨期权和看跌期权的价格。偏向通过电子表格和一个带参数‘iopt’的VBA函数来计算看涨期权和看跌期权的价格,当参数为1时计算看涨期权,为-1时计算看跌期权,这比复制看涨期权公式,然后作微小修改得出的看跌期权公式要好一些。
红利(Dividends)
初期的布莱克-舒尔斯公式并不考虑红利,但是默顿扩展了这个公式,将红利纳入考虑范围,并用于给其他期权定价,如外汇期权等。默顿在模型中将红利处理成一个连续的年红利收益q。只需将初始布莱克-舒尔斯公式中的股票即期价格S用S exp(-qT)替代就行了,和的表达式也作相应的修改。
在风险中性条件下,没有红利收入的股票只能得到无风险收益r。如果股票有红利,它的总收益仍然为r,但是该收益被分为两部分,(r-q)为股票价格收益,q为红利收益。红利将降低看涨期权的价值,因为在执行时,部分资产(红利)已经流失掉了。相反地,红利会增加看跌期权的价值,因为在执行时已损耗了资产的一部分价值。
美式期权的特征
美式期权能够在到期日或到期日前的任何一天执行。这使得二叉树方法更能满足实际需要,因为它可以很方便地处理期权提前执行的问题。而布莱克-舒尔斯公式则只能给欧式期权定价,因为它处理不了提前执行的问题。
提前执行是否能提高期权的价值,这取决于红利。例如,即使标的股票没有红利,提前执行美式看跌期权也是合理的。这种看跌期权一般会在股价大幅度下跌或利率上涨之后被执行。而对于没有红利的看涨期权,提前执行则是不明智的;但对于有红利的股票,提前执行期权则可能会获得较多的红利收入。
二叉树方法在每一个节点上拿欧式期权的价格与立即执行期权所得的回报作比较,这样就可以得到美式期权的价格了。因此,二叉树方法为美式期权提供了一个最理想的执行策略,关于美式期权定价将在第10章的后半部分介绍。
数值方法
计算统计期望值是所有期权定价数值方法的核心。随着二叉树期数的增加,正态分布与二项分布会越来越接近,正是利用这一点,二叉树方法为揭示布莱克-舒尔斯公式背后的原理作出了重要贡献。利用二叉树对股票价格运动过程建模,能够计算期权有效期内所有中间节点的收益。然后结合期权定价过程的“倒推(backward)”特性,就很容易为美式期权定价了。实际上,二叉树方法是准确并有效地为美式期权定价的基础。第10章将集中介绍二叉树理论,包括构建树的不同方法,如何利用二叉树为期权定价,以及以布莱克-舒尔斯公式为基准,测试不同树的准确性。
二叉树方法还可以看成是一种有效生成股票价格运动轨迹的方法。虽然,二叉树丢失了一些信息(例如,丢失了一些特殊的运动轨迹),但大大提高了效率。
对于依赖路径的期权,还可以用一些其他的数值方法,最典型的是蒙特卡罗模拟。对于标准期权,蒙特卡罗模拟用服从正态分布的随机变量来生成到期日的股票价格。与标准期权的二叉树方法相比,蒙特卡罗模拟的效率较低,因为它需要用随机数模拟大量路径(为了提高树的精确度)。可以在模拟时使用‘对偶变量(antithetic variates)’或‘准随机(quasi-random)’数来提高效率,后者尤其有效。而对于更加复杂的期权,蒙特卡罗模拟需要记录所有路径上的中间股票价格,这样就可以为奇异期权(此类期权的收益由标的资产价格运动的特殊路径决定)定价。第12章将集中讨论蒙特卡罗模拟。
数值方法将布莱克-舒尔斯分析的应用范围从欧式期权定价扩展到美式期权定价(用二叉树方法),并进一步到路径依赖期权定价(用蒙特卡罗模拟)。自80年代中期以来,个人计算机的处理能力得到迅猛发展,使得电子表格如Excel能够利用内嵌函数计算累积正态分布的概率。第10至13章中电子表格的所有分析解几乎都能立即算出。例如,第10章的二叉树定价(即使有1000期)结果能够在15秒内得出,而一个计算标准期权价格的蒙特卡罗模拟(10000次试验)使用Excel也不到45秒。对绝大多数标准期权来说,二叉树方法(特别是使用莱森和赖默方法)和蒙特卡罗模拟都能为其准确而有效地定价。
波动率和非正态股票收益
有关期权定价的最后一章(第13章)将集中讨论波动率,它是定价时要考虑的最重要因素,还将讨论当前处理非正态分布的方法。
布莱克-舒尔斯公式中,唯一没有直接给出的参数是股价未来的波动率。尽管可以用股价的历史数据给出未来波动率的一个预测值,但未来波动率的信息应该隐含在期权的市场价值中。因此可以通过布莱克-舒尔斯公式反推得到。隐含波动性是指能使布莱克-舒尔斯期权价格与期权市场价格相等的波动水平。因此,期权交易与波动率关系密切。如果交易者认为真实波动率将大于隐含波动率,那么他会认为期权价格被市场低估了(反之亦然)。
布莱克-舒尔斯公式建立在股票对数收益服从正态分布的基础上。但由于偏度和峰度的存在,实际股票收益的分布与严格的正态分布有所偏离。这会影响二叉树定价的精度。第13章将提出一些方法来解决非正态分布的问题,特别是鲁宾斯坦方法,它采取标准二叉树来处理非正态问题。
小结
看涨期权是一种以协议执行价买入资产的权利。而看跌期权则是一种以协议执行价出售资产的权利。欧式期权只能在到期日执行,而美式期权可以在到期日及到期日之前的任意时刻执行。
股票期权的价格取决于标的股票的当前价格、执行价格、到期日、股价波动率,以及无风险利率(和期权到期前的红利)。
单期二叉树模型是最简单的期权定价方法。采用复制投资组合的方法,可以得到对冲比率和期权价值。无须对股价上涨或下跌的概率作任何假设。
布莱克-舒尔斯定价公式给出了欧式期权定价的解析解,它假设股票的对数收益服从正态分布。
在布莱克-舒尔斯分析中,需定价的期权与一定数量的股票构成对冲投资组合。对冲比率确保该组合是无风险的,期权价值的变化将完全抵消股票价格的变化。
布莱克-舒尔斯期权定价分析同样可以用来计算‘隐含’或‘风险中性’概率,从而使风险中性定价成为可能。利用这些概率,期权的价值等于它期望收益(在风险中性条件下)用无风险利率折现的现值。
参考文献
.,1989,”How We Came Up With The Option Formula”, Journal of Portfolio Management,Winter,4-8.
Bodie,Z., and ,1996,Investment,3rd edition,Richard ,Englewood Cliffs,,.,2000,Options,Futures and Other Derivatives,Prentice Hall,New Jersey.
第10章 二叉树
最初引入二叉树是为了提供一种简单的方式来理解布莱克-舒尔斯分析。在布莱克-舒尔斯分析中,通过用二项分布近似对数收益的正态分布,可以方便地利用二叉树给股价过程建模。
在风险中性的条件下,可以通过构建对冲组合来为期权定价。这时需要计算期权收益的期望值。用三种不同的方法来构建二叉树(使用不同的参数),并将它们作对比(用提出者的名字命名:JR树,Jarrow和拉德;CRR树,考克斯,罗斯和鲁宾斯坦;LR树,莱森和赖默)。每种方法的价格相对变化量及其概率都是不同的。先以欧式期权为例,用JR树给其定价,然后介绍有名的CRR树。在讨论完二叉树定价计算的值近似收敛于布莱克-舒尔斯期权价格后,再简要介绍LR树。选择不同的参数组合似乎很困难,但选择最佳参数会比其他参数有效的多。通过用不同模型对欧式期权定价发现,利用LR树计算出的价格与由布莱克-舒尔斯公式得出的值很接近(即使在期数很少的情况下)。
二叉树因能为美式期权定价而出名,美式期权可以在到期日前(包括到期日)的任意中间时刻执行。这里将演示怎样改进CRR树使之能对美式期权定价。赫尔(2000)教科书在第9章以及第16章前面部分介绍了用二叉树方法给欧式和美式期权定价的相关背景知识。但是它完全基于CRR树。
二叉树介绍
二叉树是前面介绍的单期二叉树的扩展。例如,如果一个树有九期,在每一期股价都有向上或向下运动两种可能,则它的最终位置将由这些向上或向下运动的序列决定。股价从最初值到终值,共有512条路径,虽然只有一条路径到达股价的极值节点(即股票最高或最低价),但到达中间两个终值节点的路径则有126条。尽管最后有十个不同的终值,但到达中间终值节点的概率要比到达极值节点的概率大的多。经由各种路径到达每个终值节点的概率近似服从正态分布。另外,这种采用不同路径来得到终期价格的二叉树方法比模拟方法更有效率。
再来考虑期权定价,该树可以看成是一个有512条路径的样本空间,每条路径末端的期权收益都用到达末端节点的概率加权。如果期权不能提前执行,则将末端节点的价格按概率加权求和,从而得到期权的期望收益。将其折现即可得到期权的价值(即现值)。由于使用了风险中性定价,因此折现因子由无风险利率以及到期时间决定。
选取不同的向上(向下)运动概率以及期间长度,就可以构成不同的二叉树方法。其中有两种参数选择方法我们很熟悉。JR树假设股价向上和向下运动的概率相同(即 p=);CRR树则假设股价向上与向下运动概率的乘积为1,即d=1/u。不同的参数能够产生不同的价格树,但期权执行价格对其没有影响。
第三种参数选择方法比较少见,但却获得很高的赞誉。LR树使用了布莱克-舒尔斯中和的精确二项估计值。因此在到期日股票价格树总是以执行价为中心,并且消除了在其他两种方法中,随着期数的增加,期权价值不完全收敛的问题。因此,LR树可以看作是树方法中的布莱克-舒尔斯分析。
工作簿中包含了二叉树方法的实现过程。通过一些小型价格树例子来演示三种方法的计算过程。这些实现都是基于VBA函数的,它们还可以为那些不需要在表格中显示中间节点的大型树定价。
简化的二叉树
工作簿的第一张表(名为JRBinomial)中,显示了在价格上下运动概率相等,期间长度相同的情况下,一个九期二叉树的终值及其概率。树的结构很简单,主要是为了展示电子表格的版式设计,需要用到的主要公式,以及如何计算二叉树终值的均值和方差。这个例子还解释了怎样利用一个九期二叉树来近似标准正态分布。
图中的JR二叉树表明,九期二叉树的价格是从初始值0(单元格B18)开始运动的。在每一期,股票价格以相同的概率向上或向下运动1/3(=),见C5。K列中存放的是股价经过九期变化得到的10个不同终值。这些值是用前面的九期价格变化量相加得到,每期变化量都是1/3。每个终值的实际概率可以用二项分布计算得到。参数的选择(股价变化幅度为,p=1/2)要确保终值分布的均值为0,方差为1(要使之近似服从标准正态分布)。这样,就可以用一个简单的离散二项随机游走模型的极限——连续随机过程来对股票价格进行建模了。
Adapted JR Binomial Distribution:合适的JR二叉树分布
【参照书中第169页的图】
图 工作表JRBinomial中的九期二叉树
为了能将树放入一个方格中,树的版面被压缩了,这样能够利用单元格C18中的简单公式(在B18中已给出初值)产生树的其他部分。价格的上升将沿着对角线方向移动,价格的下降将沿着特定的行向左移动。因此在第18行中,每个数据都比前一期数据要少1/3,而沿着对角线的方向,每个数据都比前一期数据要多1/3。单元格C18中的公式使用了条件语句IF来判断单元格是否在对角线上。如果在对角线上,该单元格的值就等于对角线上前一单元格的值加1/3;如果不在对角线上,该单元格的值就等于左边单元格的值减1/3。价格单元格的相对位置由OFFSET函数来处理。单元格C18中的公式为:
在条件表达式之后,第一个表达式表示如果价格下跌,将比它最近的左单元格(同一行中)的数值少1/3(在单元格C5中),如果价格上涨,那么将比其相邻对角线上的值多1/3。OFFSET函数指向相对位移的(先沿行,后沿列)的单元格。
既然价格上涨和下跌的概率相等(因此,所有上升下降序列出现的概率相等),那么基于二叉树的概率分布可以由到达终值节点的路径数决定。在单元格C22中,给出了路径的数量(见图),所用的公式为=COMBIN($C$4,C22),其中$C$4是二叉树的期数,C22是价格向上运动的期数。在单元格I22中给出了其概率,公式为=G22*($C$6^$C$4), $C$6则是价格向上运动的概率,因为所有路径出现的概率都相同,都为。这个分布的均值为0,方差为1(见单元格I33和I34)。
【参照书中第170页的图】
图 工作表JRBinomial中的股价终值概率分布
这个例子是为下一节的JR树打基础,它为股票价格运动过程建模,从而得到期权的收益。这里有两点需要修正:首先股票价格的变化量是一个乘数,而不是一个加数,另外,需要重新调节终值节点的范围(在此均值为0,方差为1),使之匹配股票收益的要求回报率和波动率。
JR二叉树
本节用Jarrow和拉德(JR)参数来构造一个九期二叉树,并用它来为期权定价。首先,需要一个股票价格树来确定股票的终值,然后计算期权的价值,最后对期权的期望收益折现得到现值。期权的知识前面已经学过,二叉树参数的选择下面将要讨论,最后介绍怎样构建相应的Excel公式。
假设股票的现值S为100,波动率为20%(按年计算),期权为欧式看涨期权,它的执行价为95,有效期为年。还假设股票每年有3%的红利,无风险连续复利为8%(相当于年利率为%)。由于是九期模型,所以每期的时间长度是
作为简化的二叉树,JR树中股价上涨和下跌的概率相等,也就是p=。每一期上涨或下跌的幅度取决于股票价格变化乘数u和d,调整这两个值,直到n期后的均值与方差与股价收益的要求值相等。
JR European Option Value:JR欧式期权价格
【参照书中第171页的图】
图 期权信息及JR树的有关参数(表JREuro)
在JR树中,股价运动过程两项组成,第一项是风险中性的漂移项(drift term),第二项则是基于简单二叉树波动率(volatility)的波动项。漂移项已由Jarrow和拉德决定,并且股价上涨和下跌的概率相等,这能够确保在风险中性的世界中股票具有(r-q)的期望回报率。在每一期,价格乘数的期望值是,因此上涨与下跌的幅度不再相等。年波动率是一个标准差,它与期间长度的平方根相乘得到波动项。波动项确保每一期对数股票价格的方差为,因此在经过n期(或时间T)后,对数股票价格的方差为。JR树的参数可以表示为:
或者
或者
对于对数股票价格,其上涨或下跌(ln u和ln d)的幅度是一个加数,并构成一个对数股价树。而对于股价树,每一期的变化量都是用乘数因子u或d来计算。
在Excel中,计算价格上涨乘数因子u(在单元格G9中)的公式为:
它包含漂移项和波动项两个部分。
计算价格下跌乘数因子d(在单元格G10中)的公式中,波动项前用负号替代上式中的正号。
【参照书中第171页的图】
图 使用JR参数的九期股票价格树(表JREuro)
在图所以的股价树中,列号表示股价向上运动的次数(用i表示,范围从0到9),行号代表期数(用j表示,从0到9)。那么,第j期第i种状态下的股价就等于初始股价S,乘以相应次数的股价上涨和下跌乘数,例如:,在一期后有两种可能的价格变化:,等等。因此,在n期后:
这个公式具有递归性。因此,每一期的新股价仅仅依赖于前一期的股票价格和价格变化乘数,例如,,这种递归性在Excel中很容易实现,只需拷贝单元格C30中的如下公式:
=IF($A30<C$20,$G$10*OFFSET(C30,0,-1),IF($A30=C$20,$G$9*OFFSET(C30,1,-1),""))
这个公式与上一节使用的公式非常相似,只是价格运动乘数G10(下跌)和G9(上涨)是与前项相乘的。由于在单元格中使用了嵌套条件语句IF,因此公式可以处理第三种可能性,将对角线上方的单元格($A30>C$20)填充为空值“”。拷贝C30中的公式就可生成余下的树节点(并能扩展为更大的树)。通过观察偶数期的中间单元格(D29,F28……),你能看到JR树中股票价格随时间的漂移。[严格地说,这种情况仅仅在大于0时才成立,但这个条件通常满足]
由于讨论的是欧式期权,因此仅仅需要关心股价的终值,即K列中的数值。可以用下面的公式得到看涨期权对应于每个终期价格的收益:
i=0,1……9
其中,X是期权的执行价格。结果放在K49到K58中,其范围从到0。注意,凡是收益大于0的,股票价格上涨的次数一定大于或等于4次:如果小于4次,期权将不会执行。
下一步是计算看涨期权收益的期望值,并将其用无风险利率折现。因此需要看涨期权每一种收益发生的概率,这些概率与前面简单二叉树中的概率一样。它们放在K35:K44中,K44中的公式为:
=COMBIN($D$15,A44)*$G$11^$D$15
最后两项给出了任一序列的概率(),COMBIN函数给出了每一个看涨期权收益的路径数。于是10个终期收益及其相关概率如下:
状态
9
8
7
6
5
4
3
2
1
0
期权收益
0
0
0
0
概率
0,018
期权收益的期望值是各个收益的加权平均,并用无风险利率折现得到风险中性定价。因此,单元格G15中折现后期权价格的公式为:
=EXP(-D6*D12)*SUMPRODUCT(K35:K44,K49:K58)
其中,EXP(-D6*D12)是基于年无风险利率的折现因子。
因此,用九期JR树得到的看涨期权价格为(比较图中单元格G17,由布莱克-舒尔斯公式得到的价格为)。幸运的是,随着期数的增加,由JR树得到的期权价格收敛于布莱克-舒尔斯价格(这部分内容将在中讨论)。顺便说一句,G17中的布莱克-舒尔斯价格是通过用户定义VBA函数BSOptionValue得到的,代码放在Module1模块中。关于程序的解释将在第11章提到,那时将详细讨论布莱克-舒尔斯公式的细节问题。
期权标的股票的JR价格树由以下的10个值组成,并附有其相关概率:
状态
9
8
7
6
5
4
3
2
1
0
股票价格
155.16
141.20
128.49
116.93
106.41
96.84
88.12
80.20
72.98
66.41
概率
0,018
股票收益近似服从对数正态分布。利用表中的价格和概率,很容易算出分布的一阶矩M1和二阶矩M2(见单元格K9和K10)。通过M1和M2,可以利用公式(见节)计算股票对数价格(服从正态分布)的均值和方差(M和V)。这些矩的值放在图的K列中,M1和M2是用区域K21:K30中的股票终期价格计算得到的,而M和V则利用公式计算得到。可以将JR树得到的结果与布莱克-舒尔斯公式要求的股票对数价格的均值和方差作比较。可以看到,理论价格与利用二叉树方法得到的价格相当吻合。
通过验证可以知道,M1(股票价格的均值)是从S=100开始,以增长因子增长的,这个增长因子等于exp[(r-q)T]。
JREuro模型同样能为看跌期权定价。只要将D16中“iopt”参数赋值为-1,那么看涨期权就会变成看跌期权。股价树与原来的一样。但期权收益在股价跌倒执行价X以下的情况下才等于0。通过引入一个参数(iopt=1代表看涨期权,iopt=-1代表看跌期权),可以将看涨期权和看跌期权的收益公式合并为同一个表达式:
i=0,1,2……9
JR树在Wilmott etc al.(1996)中有介绍。
CRR树
工作簿OPTION1中还有两个表使用CRR树。正如CRREuro表所示,它的版面与JR树相同,只是树中使用的股票价格以及相关概率不同。CRRTheory表演示了二叉树定价的核心理论,即二项分布函数与正态分布函数相似。表中的公式能够演示,CRR树的欧式期权定价是如何随着期数的增加而收敛的。
考克斯,罗斯和鲁宾斯坦(CRR)提出了参数的可选择性,从而创建了一个风险中性的环境。价格乘数u和d只依赖于波动率和,而与漂流项无关:
这些参数反映期间内的股票价格变化波动率,但不是整个期间的变化波动率。在图的单元格G9中,计算u的公式为:
=EXP(D13*SQRT(G6))
在G10中,d的计算公式为1/u。
CRR European Option Value:CRR欧式期权价格
【参照书中第174页的图】
图 期权信息及CRR树中相关参数(表CRREuro)
观察图中的股价树,能明显看到中间的股票价格是没有漂移项的,如偶数期单元格(D29,F28,……)所示。由图中可知,CRR树是以股价现值S为中心的。
【参照书中第174页的图】
图 使用CRR参数的九期股价树(表CRREuro)
为了弥补u和d中缺少的漂移项,CRR树中的上涨概率一般大于,以保证股票价格期望值在每一期以乘数因子增加。p的计算公式为:
,其中
例子中,价格上涨的概率为(G11),因此,下降的概率为(G12),价格期望值的变化因子为(G8)。
CRR树计算期权收益的方法与JR树相同。在看涨期权最后一步定价时,G15中用于对期望收益折现的关键公式与JR树相同。经过九期变化,CRR树得到的期权价格为,而JR树计算的价格为,布莱克-舒尔斯期权价格则为。
与JR树一样,可以将参数iopt的值(D16)改为-1可以计算看跌期权的价格。注意,红利只影响CRR模型中的股票变化概率,而不影响股票价格,这与JR模型正好相反。
二项分布近似与布莱克-舒尔斯公式
迄今为止我们注意到,在电子表格中,使用二叉树和布莱克-舒尔斯得出的期权价格非常接近。CRRTheory表进一步揭示两者之间的关系,它显示了怎样用一个离散的二项分布来替代连续的正态分布N(d)。这种分布近似可应用于欧式期权。其中要用到一个‘互补的(complementary)’二项分布函数,它等于1减去通常的二项分布函数。布莱克-舒尔斯公式中的每一格N(d)都可以用互补二项分布函数替代。
图中显示的是与前一章相同的欧式看涨期权,但定义了两个新的参数。数据a(E14)表示为使股票价格最后处于‘实值状态’的最小上涨次数,也就是最终股价应大于X的次数。在图中(特别是单元格K26),可以清楚地看到四次上涨可以得到最终股价,它大于执行价95。实际上,E14中的数值4由考克斯和鲁宾斯坦给出的公式计算得到。
Theory behind Binomial Trees:二叉树背后的理论
【参照书中第175页的图】
图 表CRRTheory中N(d)的二项分布近似
另一个新参数(单元格E12中)是一个修正概率,计算公式为:
EMBED
其中的值在单元格E8中给出。
简明的CRR二项分布期权定价公式如下:
分布函数通常定义为分布左尾概率,而互补分布函数则是指右尾概率。因此,在CRRTheory表的单元格E16中,计算互补分布的概率值时应该用(α-1):
=1-BINOMDIST(E14-1,B15,E12,TRUE)
为了检测近似程度,可以用该公式计算出的期权价格(CRRTheory表的B17中)与表CRREuro的G15中的的数值作比较。
CRR二叉树的收敛性
到现在为止,所有的二叉树都限制在九期范围内(考虑到空间的大小)。但我们感兴趣的是,当到期时间固定而期数增加时,由二叉树得到的期权价格是否更准确。在不重新建立大型二叉树的情况下,一个简单的方法是使用前面提到的,并在表CRRTheory中演示过的简单期权定价公式。在期数不断增加的情况下,将这个公式计算的结果与布莱克-舒尔斯期权价格作比较,从而研究这个模型的收敛性。
下面用一张模拟运算表来说明,分别用不同期数(B15)的简单CRR期权定价公式(B17)和布莱克-舒尔斯公式(B20)来计算期权的价格,然后用图形将结果显示出来。如图所示。
在区域J4:L12中建立模拟运算表的具体操作如下。例如,在K4中输入公式=B20,在L4中输入公式=B17。在单元格J5至J12中输入不同的期数,从16到128,增量为16。然后建立模拟运算表,表格区域设为J5:J12,列输入单元格设为B5,于是就得到表格数据。然后在XY图中画出模拟运算表的输出结果,就能看出随着期数的增加,CRR二叉树计算出的期权价格在布莱克-舒尔斯期权价格附近振荡。
期权价格随着期数的增加而上下振荡,这种情况在JR树中也能看到,其产生的原因是二叉树的参数没有与期权执行价相联系。而当执行价格和初期股票价格一致时(也就是B5变为100),就不再是振荡了,而是单调收敛。单这仅仅是一个特例。
Convergence of CRR formula:CRR公式的收敛性
Option value:期权价格
Binomial steps:二叉树期数
【参照书中第177页的图】
图 随期数增加CRR树与BS期权定价比较
对于任意给定的期数,存在唯一的二叉树来描述股价运动过程。随着期数的增加,执行价格相对节点位置会不断变化。这将改变二叉树近似价格和真实布莱克-舒尔斯价格之间误差项的符号。
LR树
第三类产生股价树的参数是由Leinsen和赖默提出的。相对于JR树和CRR树选择的参数来说,他们选择的参数有两个主要优点。首先,分别估计了布莱克-舒尔斯公式中的和。其次,将到期日的股票价格集中在执行价周围,从而消除了JR和CRR树收敛过程中的振荡现象。建立LR树以及对期权定价的有关计算见图中的LREuro表。
LR European Option Value:LR欧式期权价格
【参照书中第177页的图】
图 期权信息及LR树相关参数(表LREuro)
在LR模型中,参数的选择顺序与JR和CRR模型刚好相反的,它首先确定概率,然后再确定股价运动。概率由一个反向公式得到,由此保证用二项分布来估计正态分布的精确性。概率p与布莱克-舒尔斯公式中的项相联系,概率与项相联系。例如,G9中p的计算公式为:
=PPNormInv(G8,D15)
其中,G8为布莱克-舒尔斯公式中的值。估计的精确度可以用K15中值与K16中的实际值比较得出。估计值的精确度是相似的。注意,在LR树中a的值为(n+1)/2,这能确保股价树以执行价为中心。
在股价运动的过程中,上涨和下跌的相关乘数如下所示:
其中
改变执行价格D5,并检验电子表格中修正后的树,可以看到股价树在到期日确实以X为中心。
选定参数后,LR的定价过程就与其他二叉树模型一样了(见图)。先计算期权收益,然后对其折现并求期望值,就得到期权的价格。在本例中,LR树给出了欧式看涨期权的价格(G15),这个结果与布莱克-舒尔斯期权价格(G17)非常相似。
【参照书中第178页的图】
图 LR股票价格树(表LREuro)
CRR树与LR树的比较
工作簿OPTION1的另一张工作表中,利用期权定价的VBA函数,在期数增加的情况下,比较CRR期权价格,LR期权价格与布莱克-舒尔斯期权价格的差异。图列出了期数从16到128时估计值的变化。F列是布莱克-舒尔斯期权价格(用户定义函数BSOptionValue)。它在本章被作为一个基准来使用,且与期数无关。
在相邻的两列G和H中,另一个用户定义函数BinOptionValue返回以二叉树方法计算的期权价格,输入的参数有二叉树的类型,期权类型,二叉树的期数等等。例如,函数的第一个参数imod设为1时为CRR树,2则为LR树。这些二叉树期权价格将与布莱克-舒尔斯期权价格作比较。对于给定的欧式看涨期权,LR树在期数为48时计算出的期权价格与布莱克-舒尔斯期权价格非常接近,实际上已经精确到小数点的第四位。
Euro Binomial Tree:欧式二叉树
【参照书中第179页的图】
图 不同二叉树定价与BS定价的比较
函数BinOptionValue的相关代码放在工作簿的Module0模块中,该模块中还有其他一些函数,这些函数能使二叉树定价理论实现起来更加直接。代码的特点将在中讨论。
比较不同理论得出的股价树统计量也是非常有意义的。图列出了三种不同股价树终值分布的一阶矩(M1)和二阶矩(M2),并分别计算出它们的期望(M)和方差值(V)。这些统计量都是本章用到的九期欧式期权定价的统计量,它们已经分别在图,图和图中给出。
【参照书中第179页的图】
图不同类型二叉树股价以及BS价格的统计量比较
有一个很有趣的现象:尽管LR树对于期权价格的估计更加精确,但JR树和CRR树与假定股价波动率的吻合程度要比LR树好。在到期日,股价树以执行价为中心是LR树的关键改进之处。
美式期权和CRR美式二叉树
由于二叉树定价模型不仅可以处理在到期日执行的情况,也可以处理在中间节点执行的情况,因此,它是为美式期权定价的重要数值方法。对于美式看跌期权来说,提前执行常常会增加看跌期权的价值。表CRRTree既可以为欧式期权(不能提前执行)定价,也可以为美式期权(可以提前执行)定价。计算结果很容易作比较。
为了演示,假设CRRTree表中的期权是看跌期权,也就是给D16赋值-1。图显示,不能提前执行的看跌期权价格为(H15),而可以提前执行的期权价格为(H17)。即使在没有红利的情况下,能够提前执行仍然对看跌期权有好处。由于布莱克-舒尔斯公式只适用于欧式期权,因此它只能作为欧式期权定价的基准。
CRR American Binomial Tree:CRR美式二叉树
【参照书中第180页的图】
图 表CRRTree中看跌期权提前和不提前执行情况下的定价
欧式和美式看跌期权的价格都能够从CRR股价树中得到,无论在哪一期执行,都能够根据当时的股价计算出期权的收益。
Option Payoff:期权收益
【参照书中第180页的图】
图 表CRRTree中看跌期权的期权收益树
图中49行到58行给出了这些期权收益。假设不能提前执行,相关的期权收益都在第九期实现(K49到K58中)。通过计算期权的期望收益,并用无风险利率折现得到的价格,就得到这种欧式看跌期权的价格。如前所述,欧式看跌期权的价格为(H15)(见图)。
为了给美式期权定价,不仅需要知道在第九期执行时收益,还需要知道中间任一阶段的期权收益。可以用终期期权收益(K列,图)计算第8期的期望期权价格,记住要用无风险利率对其折现。计算期望值时使用CRR概率以确保风险中性的假设成立。将第9期第i种状态下的期权收益表示为,则在第8期第(i-1)种状态下的期望收益为:
上式先计算期权收益的期望值,然后将其折现到前一期。这种计算期望收益并将其折现的过程被称为“逐期倒推(stepping back)”过程。
European Option Value:欧式期权价格
【参照书中第181页的图】
图 表CRRTree中作为欧式期权的看跌期权价格
经过九期运动后的期权收益放在K63:K72种,Excel中的逐期倒推过程可以通过将J72中的公式拷贝中整个期权价格树区域(即B63到J72)来实现:
=IF($A72<=J$62,($G$11*K71+$G$12*K72)/$G$7,"")
条件语句IF确保只有在对角线上或对角线以下的单元格才有值,这使得输出版面与股价树相似。这个带条件语句的公式计算出了期权收益在每个节点的概率加权期望值,并用单期无风险折现因子(G7)将其折现。
采用逐期倒推方法计算的九期CRR树看跌期权价格为(B72),它与我们通过终期收益分布折现得到的期望值很相似。过程如图所示。
但对美式期权而言,期权在每一期都有被执行的可能。在图中,观察单元格J72,看跌期权的期望值为,并不是图中单元格J72里的。这是因为提前执行的可能性会带来一个较大的收益。因此J86中的公式将期权的期望收益与提前执行情况下的收益作比较,并选取较大值。这个比较过程用Excel函数MAX来实现:
=IF($A86<=J$76,MAX(($G$11*K85+$G$12*K86)/$G$7,J58),"")
该公式选取收益期望值(如同计算欧式期权)与执行期权情况下所得收益(在J44中给出)之间选择一个较大值,反复运用该公式计算得到的结果如图所示,美式看跌期权的最终价格为(与图中单元格F17里显示的一样)。
American Option Value:美式期权价格
【参照书中第182页的图】
图 表CRRTree中作为美式期权的看跌期权价格
虽然对于美式期权而言提前执行是可能的,但实际上,如果股票的红利为0,对于看涨期权来说是不值得提前执行的。然而,如果看涨期权的标的股票有红利,则提前执行是可能的。作为练习,将上面的看跌期权改为看涨期权,然后求出在红利为多大时,提前执行才有价值。可以发现,只有在红利相当大的情况下,美式看涨期权和欧式看涨期权的价格才会出现明显的差异。
Module0和Module1中的用户定义函数
OPTION1中有一些用户定义函数的代码,它们实现了本章讨论的大多数二叉树期权定价计算。采用CRR树和LR树为期权定价的主要函数放在Module0中。其中最重要的公式有CRRTheory表中用到的BinEuroOptionValue函数,以及Compare表中用到的BinOptionValue函数。
在二叉树用于存放期权收益的数组中,我们让数组元素从0开始编号(而不是常用的1),二叉树的起始期数也是从0开始的,这些规则通过VBA模块顶部的Option Base 0语句中设定。另外,我们将模块页命名为Module0。
BinOptionValue函数可以分别用CRR二叉树(imod=1)和LR二叉树(imod=2)为美式或欧式期权定价。代码很容易读懂,只需设定期权的类型(如果是欧式看涨期权,则iopt=1,iea=1)以及定价方法(如CRREuro表中演示的CRR树)就可以了。定价过程中反复将向量数组vvec()定义成有十个元素的Variant类型,元素从vvec(0)到vvec(9),并用语句ReDim(nstep)来设定维数。
股价树的终期期权收益通过下面公式得到:
i=0,1,2……9
在VBA语句中用一个循环得到:
For i=0 to nstep
vvec(i)=(iop*(S*(u^i)*(d^(nstep-i))-X,0)
Next i
随后的五行代码通过二叉树执行逐期倒推过程,每一期都计算期权收益期望的折现值,并最终得到期权价格:
For j=nstep-1 To 0 Step -1
For i=0 To J
vvec(i)=(p*vvec(i+1)+pstar*vvec(i))/erdt
Next i
Next j
在VBA代码中有两点需要注意。在定价过程中,可以用新值来替代vvec数组中的旧值,这样可以节省存储空间,只需一个(n+1)×1的向量即可。还有一点是,对于美式期权(iea=2),我们还需一行代码来从欧式期权价格和立即执行所得收益之间选取一个最大值。
下一章将详细讨论对冲参数问题,因此,在这里先不介绍BinOptionGreeks函数。其中5个参数中的3个可以通过一个扩展的二叉树获得,其它两个参数则需建立两个分开的二叉树得到。函数将使用更加有效的LR树。
Module1中附有BSOptionValue函数的代码,它用来计算布莱克-舒尔斯公式。详细的解释将下一章的节给出。
小结
二叉树提供了一种实用的方法来为股价运动过程建模。终期价格的离散分布可以用布莱克-舒尔斯分析中假设的连续对数正态分布来近似。
用二叉树为股票期权定价,先要计算期权的收益期望值(用风险中性概率为每个期权收益加权),然后利用无风险利率对其折现。
二叉树定价的高效性(相对于蒙特卡罗模拟)来自于股价树中多条路径的重组。
通过选择不同的参数构建不同类型的二叉树(例如JR树,CRR树,LR树)。JR树中股价向上和向下运动的概率相等,每一期变化的幅度中包含有漂移项和波动项。CRR树的变化幅度可以反映波动率,但没有与均值有关的漂移项。为了反映股价的漂移项,需将股价上涨和下跌的概率设置成不同值,而从保证价格向上漂移(通常情况下)。LR树的终期价格是以期权执行价为中心的,它的概率和变化幅度参数都很复杂。
用三种不同类型的二叉树方法对欧式期权定价,其结果表明,LR二叉树只需较少的期数就可以得到与布莱克-舒尔斯期权价格很接近的估计值。
二叉树方法很容易处理期权提前执行的情况,因此可以用其为美式期权定价。
参考文献
Cox,J. ,S. Ross and M. Rubinstein, 1979, “Option Pricing: A Simplified Approach”, Journal of Financial Economics, 7, 229-264
Cox, J. and M. Rubinstein, 1985, Options Markets, Prentice Hall, New Jersey.
Hull, J. C., 2000, Option, Futures and Other derivatives, Prentice Hall, New jersey
Jarrow, R. A. and A. Rudd,1983,Option Pricing, Richard D. Irwin,Englewood Cliffs,NJ
Leisen, D. R. J. and M. Reimer, 1996, “binomial Models for Option Valuation-Examining and Improving Convergence”, Applied Mathematical Finance,3,319-343
Wilmott, P., S. Howison and J. Dewynne, 1996, The Mathematics of financial Derivatives, Cambridge University Press, Cambridge.
第11章 布莱克-舒尔斯公式
尽管二叉树提供了一种容易理解期权定价原理的方法,但有解析解的布莱克-舒尔斯公式仍然是欧式期权定价理论的中心内容。它的强大之处在于它是用一个公式来为期权定价,而且它可以确定复制组合的对冲比率(hedge ratio)。本章将推导布莱克-舒尔斯公式,并将其扩展到有连续红利的情况,由此可以为外汇期权和期货期权定价。还将推导出对冲参数(hedge parameter),因此,可以创建出一个在股价变化条件下价值不变的对冲组合。
赫尔教科书(2000)的期权部分是第11章内容的最好参考教材(第11章推导并解释布莱克-舒尔斯公式,第12章讨论在存在连续红利情况下对布莱克-舒尔斯公式的修正,并用其对外汇期权和期货期权定价,第13章讨论‘希腊参数(greeks)’和delta对冲系数)。演示不同布莱克-舒尔斯定价公式的模型放在工作簿中,并附有许多有用的定价函数。
布莱克-舒尔斯公式
节已经介绍过布莱克-舒尔斯公式,节还简要介绍了存在红利情况下的期权定价问题。本节利用默顿方法对节中的公式进行扩展,使之适用于存在连续红利的情况。
默顿扩展方法将不存在红利和每年有q%连续红利收益的情况作比较。在风险中性的世界里,这两种股票应该具有相同的总收益,即红利和资本增长。如果有红利收益的股票在时间段T内从初始价格S增长到,那么对于无红利股票,就应该从S增长到,也可以说,从S exp(-qT)增长到。因此,的概率分布可以适用于以下两种情况:
初试价格为S,并有q%的连续红利收益
初试价格为S exp(-qT),但没有红利收益
因此,如果一个欧式期权的标的股票以连续收益率q来支付红利,那么在为它定价时,可以用S exp(-qT)代替原来的初试值S,然后将该股票看作不支付红利的股票。
于是,对一个支付红利的欧式看涨期权来说,其布莱克-舒尔斯公式为:
其中q是连续红利收益率,N(d)是累积的标准正态分布函数,并有:
为了便于解释布莱克-舒尔斯公式各项内容的意义,可以联想到看涨期权的复制组合形式,c=hS-B。公式第一项是乘数S(也就是‘对冲比率’),等于。第二项则是执行价格的现值与的乘积。因此,可以看成是在风险中性世界里看涨期权被执行的概率。
利用期权平价关系,看跌期权在支付连续红利情况下的布莱克-舒尔斯定价公式为:
它可以写成如下形式:
如果没有公式前的负号以及累积正态分布函数内的负号上,则该式与看涨期权定价公式完全一样。
在Excel中运用布莱克-舒尔斯公式
图中是前面用过的看涨期权实例,以及布莱克-舒尔斯定价所需的计算。最初计算时,常用多项式来近似累积正态分布概率。现在,可以用Excel的NORMSDIST函数直接得到。得出和后,就可以计算相应的和了,它们分别放在E11和E16中。由于它们都是概率值,所以取值范围介于0和1之间。
Black-Scholes Formula(extended to allow for continuous dividends):
布莱克-舒尔斯公式(考虑了连续红利情况下的扩展型)
【参照书中第186页的图】
图 表BS中的布莱克-舒尔斯期权定价过程
单元格E5中的布莱克-舒尔斯公式为:
=B4*B16*E11-B5*B15*E16
其中,B15和B16中的是两个折现因子,而E11和E16中的则是累积正态分布值,它们都是计算过程的中间值。E5中看涨期权的价格为。也可以用用户定义函数BSOptionValue计算布莱克-舒尔斯期权价格,结果放在E8中,相关代码将在中讨论。
从前面的讨论中可知,对冲比率是exp(-qT)与的乘积。在这里等于(也就是*)。期权在风险中性世界里被执行的概率为,即。
对于相同标的股票的看跌期权,H5中的布莱克-舒尔斯定价公式为:
=B4*B16*(E11-1)-B5*B15*(E16-1)
看涨期权定价公式的第一项是,由于正态分布的对称性,看跌期权定价公式的第一项则为。因此看涨期权公式中的E11被(E11-1)代替。用同样的方法处理第二项。H5中看跌期权的价格为。同样,在H8中存放用户定义函数BSOptionValue计算出来的看跌期权价格。这个函数有个重要的参数iopt,取值为1代表看涨期权,取-1则代表看跌期权,这样就可以用一个通用函数来代替两个分开的函数(分别计算看涨期权和看跌期权的价格)。可以看出,看涨期权和看跌期权定价的代数表达式非常相似,仅在一些符号上有差异。
为了研究期权价格的影响因素,首先必须弄清楚期权与标的股票之间的因果关系。你将发现期权价格对标的股票的波动率变化非常敏感。前面已经提到过(第节),这种敏感性分析用一个或多个模拟运算表(Data Table)很容易实现。
外汇(Currencies)和商品(Commodities)期权
到目前为止,所有的讨论都集中在股票期权方面。事实上,基于连续红利收益的布莱克-舒尔斯分析框架同样可以用于为外汇和商品期货期权定价。我们已经知道,对于连续红利收益率为q的股票看涨期权,布莱克-舒尔斯定价公式为:
可以将外汇看成是一个支付连续红利的股票,外汇利率R则看成是布莱克-舒尔斯公式中连续红利收益率q。本国利率即为无风险利率r。因此,外汇看涨期权的定价公式为:
这就是加曼-科尔哈根公式(加曼和科尔哈根,1983)。图显示的是Currency表中的一个外汇看涨期权定价例子,它的外汇利率为4%。期权价格为(E11)。这个例子由用户定义函数BSOptionValue通过适当的参数计算得到。
Valuing Options on Currency Forwards(As shares with a continuous dividend yield):
为外汇远期期权定价(如同股票带有连续红利的情况)
【参照书中第188页的图】
图 表Currency中的外汇期权定价
图还演示了一种由布莱克(1976)提出的代替方法。他建议将外汇期权的价格用远期汇率F表示,而不是即期汇率S。由利率平价理论可知,在定价公式中可以用F来替代S。因此,布莱克给出的看涨期权定价公式为:
如果用F/X表示,则和的表达式被都很简单。单元格E13中用布莱克方法计算出的结果为。已经编写了一个用户定义函数BlackOptionValue来实现布莱克方法(看单元格E14)。
布莱克公式同样适用于为商品期货期权定价,图中的表Commodities演示了这种方法。注意,‘看跌期权-看涨期权-远期合约(put-call-forward)’的平价关系也是成立的。因此,当远期或期货的价格F等于期权的执行价格时,看涨期权的价格等于看跌期权的价格。(图说明了这点)
Valuing Options on Commodity Futures(As shares with a continuous dividend yield):
为商品期货期权定价(如同股票带有连续红利的情况)
【参照书中第188页的图】
图 表Commondities中的商品期货期权定价
计算期权的‘希腊’参数
布莱克-舒尔斯的输入参数有股票现值S,利率r,期权有效期,波动率,及其他一些因素。研究输入变量变化对期权价值的影响时,一种办法是计算期权的所谓“希腊”参数,或对冲参数。经常计算的对冲参数是一些一阶偏导值:delta(描述股价变化的影响),rho(描述利率变化的影响),theta(描述期权有效期变化的影响),vega(描述波动率变化的影响);也经常计算股价的二阶偏导值gamma。除了theta外,所有的对冲参数都由公式直接给出。布莱克-舒尔斯偏微分方程将thera与期权价格,delta值,gamma值联系起来。
Option Greeks and Hedging:期权的‘希腊’参数以及对冲
【参照书中第189页的图】
图 表Hedge中的‘希腊(greeks)’参数计算
图显示的是常用看涨期权的有关“希腊”参数 见表Hedge。图中,看涨期权的delta值为。这就是说,当股价发生微小变动,期权价格的变化幅度是股价变化量的72%。由于它是基于的,所以看涨期权多头(long)的delta值总是处于0,1之间。相应地,看跌期权多头的delta值则依赖于,因此它总是负值。
公式给出的delta值是股价为100时期权价值的瞬时变化率。可以通过电子表格来观察股价S发生微小变化时,期权价值的实际变化量。例如,当股价为101时,看涨期权的价格是,暗示实际delta值为;当S值为110时,看涨期权的价格为,暗示实际delta为。如果股价变化,对冲比率也将发生变化,对冲组合也需要重新构建。
gamma值等于股价变化时delta值的变化率(也就是看涨期权价格对股价的二阶偏导)。它的计算公式对看涨期权和看跌期权都是一样的。如果gamma值较小,delta的变化量也就非常小,如图所示。
对于看涨期权和看跌期权而言,theta都是负值。它度量期权价格随时间流失(即期权有效期减少)的变化率。当期权有效期减小时,期权价格也会减小。
另一方面,随着波动率的增加,期权的价格也会随之增加。Vega用来度量期权价格相对于波动率的变化率,它是一个正值。而且,计算vega的公式对于看涨期权和看跌期权来说是一样的。
投资银行常常构造对冲组合来抵消他们面临的期权风险。他们感兴趣的是,在股票价值以及波动率等因素变化时,整个头寸价值将如何变化。期权相对于股价以及其他因素变化的敏感度(也就是‘希腊’参数)常用来来构造对冲组合,具体情况将在下一节演示。
对冲组合
计算对冲参数是构造对冲组合必不可少的一步。利用前面用于计算‘希腊’参数的看涨期权,我们来构造两个零投资对冲组合(zero-investment hedge portfolios)。所谓零投资组合,是指相对于股价的变化,组合价值的变化非常微小。第一个是delta对冲组合,也就是说,它可以对冲掉股票价格的微小变化(被称为delta风险)。另一个是delta-gamma对冲组合,它用于对冲股票价格的较大变化,此时gamma值会发生改变(被称为gamma风险)。
将看涨期权定价公式(c=hS-B)写成0=hS-B-c的形式,由此可以得到一个零投资组合,它包括一些借入资金,用来购买一定数量的股票并出售一份看涨期权。由于这个组合是零投资组合,因此在每一期股价S发生微小变化时,这种数量关系必须保持平衡。在delta中性(delta-neutral)的情况下,购买的股票数量必须等于组合中看涨期权的delta值。构造delta中性组合的目的在于用期权价值的变化来抵消股票价值的变化。
Creating a delta-neutral portfolio :构建一个delta中性组合
【参照书中第190页的图】
图 表Hedge中的delta中性组合
图显示了一个由股票和看涨期权空头构成的delta中性组合,标的股票和期权的信息在图中给出,对冲比率也在图中给出。组合显示在图的单元格区域E25:E27中,它包括买入份股票(E22中)和卖出1份看涨期权,所需的现金为(股票支出减去卖出看涨期权所得)。当未来股价在一周的时间里不断变化(E21中S1)时,我们来观察初始delta中性组合的价值是如何变化的。如果下一周的股价S1等于102,组合仍包含份股票,股票的价值为,借入的资金为(包含利息),此时看涨期权的价值为(期权有效期变短带来的价值变化抵消了股价的变化)。对于股价的这种小幅变化,不需要新的投资就可保持近似的delta中性。但如果股价发生了较大变化,则期权的delta值将会改变,此时组合中的股票份数也需要调整,以使组合达到新的平衡。
为了构造一个更好的对冲投资组合以面对更大的未来股价变化,可以在组合中加入另一种看涨期权,从而构造出一个delta-gamma组合以满足delta中性,形式为:
其中和为两种看涨期权的价值。假设第二种期权的有效期为9个月,执行价为100,如图所示。看涨期权的gamma值在E37中计算()得到。当第二种看涨期权的数量为k时,即可消除组合的gamma风险。另外,通过选择适当的股票系数h(在单元格E36中计算得到),就可以组合的消除delta风险。借入或贷出资金的数量(在E38中)可以保证该组合为一零投资组合。同样,我们可以观察这个delta-gamma中性组合的价值在一周内随股价(E21中S1)的变化情况。
Creating a delta-gamma neutral position :构建一个delta-gamma中性头寸
【参照书中第191页的图】
图 表Hedge中的delta-gamma中性组合
如果一周后的股票价格为S=102,对比delta和delta-gamma两个组合,会发现这两个组合都是零价值组合,也就是说,它们都不需要追加投资来保持平衡。但如果股价的变化较大,如从90到110,则图显示,在这种情况下delta值需要不断调整以使组合达到平衡,这远比delta-gamma组合要复杂。
Hedge performance:对冲表现
Portfolio value:投资组合价值
Share price:股票价格
【参照书中第192页的图】
图 delta对冲组合和delta-gamma对冲组合的结果比较
布莱克-舒尔斯公式的正式推导
由于布莱克-舒尔斯公式在本章中处于中心地位,因此有必要对它的推导(采用尼尔森方法,1992)作一个简要的说明。前面已经提到,有连续红利收益的股票看涨期权定价公式为:
要得到这个公式,必须假设股票在到期日T的对数收益服从正态分布。这意味着随机变量可以写成一个随机方程的形式:,其中是均值为,方差为正态分布变量。
在风险中性概率测度Q下,看涨期权的价值等于其期望收益的折现值,即:
这个表达式能写成以下两项之和:
由于,因此可以将第二项的期望值表示为X。这个简化表达式说明,期权只有在的值大于ln(X/S)的情况下才会被执行。由于服从均值为方差为的正态分布,并且:
可以将转换成一个标准正态分布变量,只需减去它的均值然后除以它的标准差即可,即:
因此~N(0,1)。
累积正态分布函数N(d)给出了分布左尾概率值,也就是。如果是右尾概率,即,则可以根据正态分布的对称性用N(-d)计算得到。
于是可以将转换成的形式:
这与的表达式相同,由此得到布莱克-舒尔斯公式的第二项。
布莱克-舒尔斯公式第一项的期望值则比较复杂,因为条件期望的两侧都有随即变量。要推导布莱克-舒尔斯公式,需要用到关于对数正态和正态变量的两个结论,一个大家很熟悉,另一个则比较陌生。
第一个结论是:如果服从正态分布,其均值为M,方差为V,那么:
其中X是一个数值,N()是累积标准正态分布函数。条件期望被一个普通期望值和一个正态分布函数的乘积代替。由于的均值,方差为,因此:
下一步使用大家比较熟悉的关于对数正态分布的一个结论:如果是一个服从正态分布的变量,均值为M,方差为V,那么。这里,是一个正态变量,其参数为:,,因此:
由此可得到布莱克-舒尔斯公式的第一项。
在以上的推导中,能够看出()和间的关系。由于期权仅在股价大于执行价X的条件下才会执行,所以这种关系是反映股价分布被截尾情况下的一种调整,且调整幅度与股价增量的标准差有关。
Module1中的用户定义函数
该模块页中存放着用于计算布莱克-舒尔斯欧式期权价值的VBA函数代码,及相关的‘中间’函数BSDOne和BSDTwo代码。
函数BSDOne返回期权公式中的值,它的输入参数有:股票现值S,期权执行价格X,利率r,红利率q,以年计的有效期(tyr)及波动率(sigma)。函数BSDTwo返回值,输入参数与前者类似。
将这些‘中间’函数与BSOptionValue函数分开编写的目的,是为了使计算过程更加简洁清晰。NDOne使用了Excel函数NORMSDIST,其中由函数BSDOne计算得出。还有一些语句用来确保程序能够识别并截获一些不合理的数据(如负的股价),返回一个错误值(在此为-1)。
BSOptionValue函数通过参数‘iopt’来区别看涨期权和看跌期权,值为1时处理看涨期权,值为-1时处理看跌期权。由于存在期权平价关系,看涨期权和看跌期权的定价公式除了在符号上有区别外,其他地方都是一样的。分别为看涨期权和看跌期权定价时,变量NDOne和NDTwo的符号须作改变,就像定价公式那样。相关代码如下所示:
Function BSOptionValue(iopt,S,X,r,q,tyr,sigma)
’ returns the Black-Scholes value(iopt=1 for call,-1 for put;q=div yld)
’ uses BSDOne fn
’ uses BSDTwo fn
Dim eqt,ert,NDOne,NDTwo
eqt=Exp(-q*tyr)
ert=Exp(-r*tyr)
if S>0 And X>0 And tyr>0 And sigma>0 Then
NDOne=(iopt*BSDOne(S,X,r,q,tyr,sigma))
NDTwo=(iopt*BSDTwo(S,X,r,q,tyr,sigma))
BSOptionValue=iopt*(S*eqt*NDOne-X*ert*NDTwo)
Else
BSOptionValue=-1
Endif
End Function
为远期和期货期权定价的函数只是BSOptionValue函数的改进版本,它需要用变量rfgn表示外汇利率:
Function BlackOptionValue(iopt,F,X,r,rfgn,tyr,sigma)
’ returns Black option value for forwards
’ uses BSOptionValue fn
Dim S
S=F*Exp((rfgn-r)*tyr)
BlackOptionValue=BSOptionValue(iopt,S,X,r,rfgn,tyr,sigma)
End Function
计算每个对冲参数的函数可以直接复制电子表格中的公式。有一个复合函数BSOptionGreeks可以用来计算所有的对冲参数,以避免一些不必要的重复代码。它通过IF语句来判断整型变量igreek要求返回的是哪一个参数:
Function BSOptionGreeks(greek,iopt,S,X,r,q,tyr,sigma)
’ returns BS option greeks (depends on value of igreek)
’ returns delta(1),gamm(2),rho(3),theta(4) or vega(5)
’ iopt=1 for call,-1 for put; q=div yld
’ uses BSOptionValue fn
’ uses BSDOne fn
’ uses BSDTwo fn
’ uses BSNdashDone fn
Dim eqt,c,c1,c1d,c2,d,g,v
eqt=Exp(-q*tyr)
c=BSOptionValue(iopt,S,X,r,q,tyr,sigma)
c1=(iopt*BSDOne(S,X,r,q,tyr,sigma))
c1d=BSNdashiDOne(S,X,r,q,tyr,sigma)
c2=(iopt*BSDTwo(S,X,r,q,tyr,sigma))
d=iopt*eqt*c1
g=c1d*eqt/(S*sigma*Sqr(tyr))
v=-1
if igreek=1 Then v=d
if igreek=2 Then v=g
if igreek=3 Then v=iopt*X*tyr*Exp(-r*tyr)*c2
if igreek=4 Then v=r*c-(r-q)*S**(sigma*S)^2*g
if igreek=5 Then v=S*Sqr(tyr)*c1d*eqt
BSOptionGreeks=v
End Function
小结
为欧式期权定价的布莱克-舒尔斯公式可以扩展用来处理有连续红利收益情况下的期权定价问题。只需在公式中用S exp(-qT)代替S即可。在实际中,股票并不能提供连续的红利收益。但可将其它一些类型的资产近似作为支付连续红利的股票看待,因此,这种修改后的版本扩大了布莱克-舒尔斯公式的应用范围。布莱克-舒尔斯框架同样适用于为外汇和期货期权定价。
期权的delta值是期权价格相对于股价的变化率。通过delta值,可以构造短期的delta中性投资组合。但由于组合的delta值会随时间变化,因此组合中标的股票的头寸需要不断调整以达到新的平衡。
期权价格相对于其它因素的敏感度(如波动率,有效期和收益率等)同样可以计算得到。它们统称为‘希腊’参数,它们对构建对冲组合很重要。
参考文献
Black,F., 1976, “The Pricing of Commodity Contracts,” Journal of Financial Economics, 3, 167-179.
Garman, M. B. and S. W. Kohlhagen, 1983, “Foreign Currency Option Value,” Journal of International Money and Finance, 2, 231-237
Hull, J. C., 2000, Options, Futures and Other Derivatives, Prentice Hall, New York.
Nielsen, L. T., 1992, “Understanding N(d1) and N(d2): Risk-Adjusted Probabilities in the Black-Scholes Model,” INSEAD Working Paper 92/71/FIN.
第12章 欧式期权定价的其它数值方法
本章将讨论两种用于为欧式期权定价的数值方法(蒙特卡罗模拟和数值积分(numerical integration))。它们可以代替二叉树方法来计算期望值。但对美式期权,仍建议用二叉树方法给标准期权定价。
蒙特卡罗模拟是一种非常成熟的技术,它广泛运用于各种领域。但对于期权定价来说,期权定价时, 普通随机抽样蒙特卡罗模拟的效率相对较低(特别是与二叉树方法相比较)。例如,为了使抽样误差减少一半就需要4倍数的模拟试验。因此,最近在金融领域出现的准随机序列技术(quasi-random number sequences),以及计算机性能的提高,大大推进了蒙特卡罗模拟的应用。模拟方法被认为是为路径依赖(path-dependent)期权定价的最好数值方法。关于模拟的相关背景资料请阅读赫尔(2000)书中的期权部分(第16章)。
本章首先用普通蒙特卡罗模拟为欧式看涨期权定价,然后使用对偶变量(antithetic variates)模拟,最后使用准随机序列进行模拟,并将它们作比较。使用对偶变量的目的是通过减小模拟结果的标准差来提高估计的效率。准随机抽样能进一步有效控制模拟结果的随机性。这三种方法在的不同表中实现,第四张表中给出结果比较。
接下来演示数值积分方法。前面已经间接提到过,布莱克-舒尔斯期权定价公式来源于连续积分。(正如在第九章中提到的,该公式是通过解一个偏微分方程得到的)。二叉树提供了一种离散近似方法,而在此将使用中值定律更直接地进行数值积分。
蒙特卡罗模拟介绍
蒙特卡罗模拟进行期权定价的核心在于生成股票价格的随机过程。节中,在期权到期的T时刻,标的股票价格的随机方程为:
其中,随机变量服从标准正态分布,即服从N(0,1),随机变量服从正态分布,其均值为,方差为,为股票的收益率,为股票的波动率。期权的收益依赖于在风险中性世界里的期望值,因此对于风险中性定价,股票的收益率()可以用无风险利率r减去连续红利收益率q代替,也就是(r-q)。于是风险中性定价的随机方程为:
其中服从标准正态分布。上式中的股价运动过程与前面二叉树定价中的一样。
蒙特卡罗模拟随机产生一组股价终值的样本值,即模拟试验。然后为每一个样本值计算期权收益并记录下来。产生足够多的样本值后,就可以得到期权收益的分布,通常需要计算分布的均值和标准差。模拟试验的代数平均值常用来估计期权收益分布的期望值,然后用无风险利率对其折现来得到看涨期权的价格。
图为工作簿中MC1表的一部分,表中欧式期权的有效期是六个月,其标的资产是连续红利收益率为3%的股票。表中有36个期权收益的模拟试验,用它们可以估计出期权收益期望值的折现。
Using Monte Carlo Simulation to Value BS Call Option:
利用蒙特卡罗模拟来为布莱克-舒尔斯看涨期权定价
【参照书中第198页的图】
图 期权信息及5个(从36个模拟数据得到)期权收益模拟结果(表MC1)
每个模拟试验产生一个终值股价(的一个样本值)和一个期权收益值。在B列中用Excel的RAND函数来产生服从均匀分布的随机数,然后在C列用标准正态分布函数NORMSINV将其转换成随机样本。RAND函数产生[0,1]间服从均匀分布的随机数。将其作为累积概率值(值在0到1之间),用NORMSINV即可得到服从标准正态分布的随机变量值,其结果大部分处在-3与3之间。例如,第一次模拟试验C22中的公式为:
=NORMSINV(B22)
其输入值为(大约10%),产生的标准正态变量的值则为。
得到随机样本值(),就可以用下面公式计算期权到期日的股票价格:
为了将其转换为单元格公式的形式,有必要先计算出T时刻的风险中性漂移项和波动率,也就是和(分别处于B16和B17中)。因此,E22中的公式为:
=$B$4*EXP($B$16+C22*$B$17)
相应的期权收益为(H22):
=MAX($E$4*(E22-$B$5),0)
E4中存放的是参数iopt,它用来区分看涨期权和看跌期权。
计算模拟出的36个期权收益的平均值,然后折现即可得到看涨期权价值的估计量(E9)。用于折现的风险中性因子(exp(-rT))放在B18中。
图显示,期权价格的蒙特卡罗估计值()与布莱克-舒尔斯期权价格有较大的差异。E10中,期权价值估计值的标准差(模拟期权收益的标准差除以模拟次数的平方根)相对较大(这就是蒙特卡罗估计值与布莱克-舒尔斯期权价格有较大差异的原因)。为了提高蒙特卡罗估计的准确度,有必要增加模拟试验的次数。
在Excel中按下F9,就可以产生另外36个模拟值,并得到一个不同的蒙特卡罗期权价格以及相应的估值标准差。对于看跌期权,单元格公式同样适用。将参数iopt(E4中)改为-1,就可以计算看跌期权的蒙特卡罗估计值,可将它与布莱克-舒尔斯期权价格作比较。
对偶变量(Antithetic Variables)模拟
除了增加模拟试验的次数,另一个提高蒙特卡罗估计精度的方法是使用所谓的‘对偶’变量。对偶变量方法用一对负相关变量来生成一对负相关模拟样本。如果将每对结果取平均值,则模拟结果的可变性将比普通随机样本小。
图显示了表MC2中的一些模拟结果,正态随机样本(C22中的)用来生成两个股票价格,其中D22中的值来自服从N(0,1)分布的(C列中),E22中则来自其相反数-。由于这一对股票价格是严格负相关的,因此模拟的期权收益payoff1和payoff2也是趋于负相关。H22由二者的平均得到。可以看出,用这种技术得到的模拟结果(H列中)的可变性要明显小的多。
【参照书中第200页的图】
图 表MC2中运用对偶变量方法的5次期权收益模拟结果
将H列中的36个模拟结果取平均值,然后用无风险利率折现即可得到看涨期权的价值(B18),如表MC1中所示。
MC2中的情况基本与普通蒙特卡罗模拟一样。但应该注意,MC2中蒙特卡罗模拟(使用对偶变量)估值的标准差要比MC1表中的小的多。
准随机抽样(Quasi-Random Sampling)模拟
实际应用中,不加控制的随机变量显得过于随机。例如,一个均匀分布的随机序列经常聚在一堆。解决这种聚集现象的方式之一是产生一个随机序列,使之均匀的分布于单位间隔中。准随机序列以确定的方式提前设定一组数,从而消除随机数的聚集现象。唯一的技巧是在选择新数时不要选择已经入选的数据。用准随机抽样意味着样本估计值的误差与而不是成比例,n为样本容量。QMC表(用QMC表示Quasi-蒙特卡罗)中显示了生成准随机序列的结果,并介绍一种将其转化为标准正态变量的改进方法。该方法使用福勒序列而不是均匀随序列,并使用了由莫罗提出的修正转化方法。莫罗将传统的博克斯-马勒转换方法做了改进,因为一些正态分布函数的反函数容易打乱福勒序列的均匀间隔。生成福勒序列并将其转化为正态分布变量是通过VBA用户定义函数实现的。这些函数的代码,以及Module1模块页中的其他用户定义函数将在节解释。关于福勒序列,需要特别说明的是,为了与实际相符合,福勒序列是从(16)开始的,以避免初始值问题(start-up problem)。
在图中,第22行的数据为第一次模拟试验的结果,其中B22为第一个准随机数,它在FaureBase2函数输入值为16的情况下产生。C22使用MoroNormSInv函数将其转化为标准正态变量。股票价格(E22)和期权收益(H22)的计算与MC1表中相同,E9中给出了期权价值,其标记为‘QMC value’。该估计值为(标准差为,见E10),它与布莱克-舒尔斯公式得到的并不是很接近。但在节,将看到使用准随机抽样方法的最大好处是,随着模拟次数的增加,所得的结果收敛于真实数据的速度是最快的。
Using Monte Carlo Simulation to Value BS Call Option(Quasi-Random Numbers):
利用蒙特卡罗模拟来为布莱克-舒尔斯看涨期权定价(准随机数)
【参照书中第201页的图】
图期权信息及5个(从36个模拟数据得到)期权收益模拟结果(表QMC)
注意,按下F9重新计算时,表中数据没有变化。福勒序列是完全确定的,而不是随机的,因此,名称‘准随机’在此显得并不恰当。因此有必要用不同次数的模拟试验来得到不同的QMC价格和标准差。
与前面的表类似,该定价同样适用于看跌期权。只需改变参数iopt的值(-1)即可得到看跌期权的QMC估计值,可以将估计值与布莱克-舒尔斯公式所得值进行比较。
准随机抽样可以看作是分层抽样(stratified sampling)的发展。对分层抽样来说,间隔将分的更细,并有一定数量的样本随机分布在每个间隔中,故在小间隔内会存在数据聚集现象。而在准随机数序列中却没有任何随机因素,因为对序列中的每一个值,它的相对位置已经预先确定。
模拟方法比较
对于前面提到的三种抽样方法,随着模拟试验次数的增加,观察看涨期权价值的收敛性是很有必要的。图列出了模拟次数从100增加到2000时的对比结果。布莱克-舒尔斯价格提供了一个基准值(由函数BSOptionValue计算得到,位于F列)。G列中的看涨期权价值是用控制抽样法(通过对偶变量)由函数MCOptionValue计算得到的。H列中的数据则是用准随机抽样方法用函数QMCOptionValue计算得到的。
Comparing Monte Carlo Simulation Methods to Value BS Call Option:
比较为布莱克-舒尔斯看涨期权定价的不同蒙特卡罗模拟方法
【参照书中第202页的图】
图 蒙特卡罗随机和准随机抽样的估计值与BS价值的比较
如果将区间E6:H17中的数据用折现图表示(如图所示),可以发现,QMC估计值的收敛性相对于MC估计值有较大的改进。当模拟次数增加时,QMC估计值以‘二次’速率收敛,这与第10章提到的LR二叉树的收敛特性相似。
Convergence of MC estimates to BS value:BS价值蒙特卡罗估计值的收敛情况
Call value:看涨期权价值;
Number of simulations:模拟次数
【参照书中第203页的图】
图 MC和QMC抽样估计值相对于BS价值的收敛性
蒙特卡罗 模拟中的希腊参数计算
大多数教科书都认为估计对冲参数的最好方法是使用有限差分近似(finite difference approximations),每次近似,输入参数的一个微小改变,都需要一个额外的模拟试验。这种方法得到的是一个有偏估计,并且消耗时间。
布罗迪和格拉瑟曼(1996)提出了怎样在一次模拟试验下得到直接的顺向估计值。这种估计是无偏的, 且比有限差分近似更快得到结果。用户定义函数QMCOptionGreek135(参数为igreek)中包含有这些公式的代码,存放于Module1中。函数使用准随机正态变量(-qrandns),并调用FaureBase2函数以及MoroNormSInv函数。可返回delta值(igreek=1),rho值(igreek=3)或者vega值(igreek=5)。
数值积分
数值积分是另一种众所周知的可用于期权定价的数值方法。在此只介绍最简单一种积分程序,即扩展的中值定律(midpoint rule)。该方法将区间分为一组等宽的间隔(假设宽度为h),并用间隔的中值对函数进行积分,然后估计及相应的期权收益。每一个值的概率用间隔h与该间隔中值的标准正态密度函数值的乘积来近似。用该概率对期权收益加权平均,就可以得到期权的期望收益。由此可以看出,数值积分的这种形式与二叉树中期望值的计算非常相似,只是在这里使用的是正态分布函数。
图显示了NI表中的部分内容。表中,的可能取值范围被分成36个等宽区间,跨度从-6倍的标准差到+6倍的标准差(偏离均值的距离),因此每个区间的宽度为1/3倍的标准差(因此h=)。每个区间的中值放在B列中,用偏离均值的标准差倍数表示,相应的到期日风险中性股票价格放在C列中,C21中第一个值的计算公式为:
=$D$4*EXP($D$15+$D$17*B21)
Using Numerical Integration to Value BS options:利用数值积分方法来为BS期权定价
【参照书中第204页的图】
图 用中值定理计算数值积分(表NI)
相应的期权收益放在E列中。F列存放的是概率,F21中的公式为:
=$H$17*NORMDIST(B21,0,1,FALSE)
参数FALSE表示该函数返回的是概率密度值,即,均值左边倍标准差距离处的正态曲线高度。用概率密度乘以区间宽度h,即可得到一个近似的概率。这个概率用来计算期权收益的期望值,就像前面二叉树中用到的节点概率一样。在H列中存放的是收益值与对应概率的乘积,对其求和并用无风险折现因子折现即可得到期权价值。单元格H9中的所谓‘NI value’为,它比较接近布莱克-舒尔斯公式所得值。
对于图中显示的z值,当概率为0时,H列中得到的乘积也为0。但当i从17到30时,这个乘积就不为0了。图确认了这一点,用偏离均值不同距离的概率和相应的期权价值,这两者的乘积可以期权收益的期望值。
Components of Expected Value:期望收益的组成部分
【参照书中第205页的图】
图 概率及收益值,二者构成期望收益
上面的例子只是用数值积分方法为简单的期权定价,它相当的简单。数值积分真正盛行的原因是它可以为收益依赖于多种资产的期权定价。
Module1中的用户定义函数
为了方便电子表格计算,已经编写好了用户定义函数来实现本章讨论所有数值方法。节中已经见过MCOptionValue函数和QMCOptionValue函数,还比较了两者的相对收敛速度。以下主要讨论它们的代码。
MC2表中的MCOptionValue函数使用了一个循环结构来进行一系列模拟试验。变量‘sum’用来存储期权收益的和,这里使用对偶变量方法得到两个股票价格。股票价格依赖于时间‘tyr’内的漂移项(用变量‘rnmut’表示),以及波动率(用‘sigt’表示)。直接模拟股票对数价格可能速度更快,但为了清楚起见,我们仍然模拟股票价格。代码的关键部分如下所示:
Rnmut=(*sigma^2)*tyr
sig=sigma*Sqr(tyr)
sum=0
For i=1 To nism
Randns=(Rnd)
S1=S*Exp(rnmut+randns*sigt)
S2=S*Exp(rnmut-randns*sigt)
payoff1=(iopt*(S1-X),0)
payoff2= (iopt*(S2-X),0)
QMCOptionValue函数代码中除了调用FarueBase2函数和MoroNormSInv函数的语句外,其他部分与上面的代码相似,这两个函数替代了Excel和VBA函数的功能(在MCOptionValue中用到),用来生成随机样本。主要代码如下:
sum=0
For i=1 To nsim
qrandns=MoroNormSlnv(Faure1Base2(i+iskip))
S1=S*Exp(rnmut+qrandns*sign)
sum=sum+(iopt*(S1-X),0)
Next i
QMCOptionValue=Exp(-r*tyr)*sum/nsim
福勒序列需要将10进制整数转换成其他进制的数。在此选择2进制。将2进制数反转,最终得到一个由1/2幂表示的分数。
Function FaureBase2(n) Double
’ returns the equivalent first Faure sequence number
Dim f As Double, sb As Double
Dim i As Integer, n1 As Integer, n2 As integer
n1=n
f=0
sb=1/2
Do While n1>0
n2=Int(n1/2)
i=n1-n2*2
f=f+sb*i
sb=sb/2
n1=n2
Loop
FaureBase2=f
End Function
QMCOptionGreek135函数使用布罗迪和格拉瑟曼公式计算三个希腊参数(delta,rho,vega)。Gamma不是一个随机数,它是确定的,可以用前面BSOptionGamma函数(工作簿中)中的相同公式计算得到。正如前面提到的,用模拟出的期权价值,以及delta和gamma估计值,就可以计算出theta参数:
ert=Exp(-r*tyr)
rumut=(*sigma^2)*tyr
sigt=sigma*Sqr(tyr)
r1=(r-q+*sigma^2)*tyr
iskip=(2^4)-1
greek=0
vg=-1
For i=1 To nsim
Qrandns=MoroNormSlnv(Faure1Base2(i+iskip))
S1=S*Exp(rnmut+qrandns*sigt)
If(igreek=1 And Sgn(iopt*(S1-X))=1)Then greek=greek+S1
If(igreek=3 A nd Sgn(iopt*(S1-X))>=1)Then greek=greek+S1
If(igreek=5 A nd Sgn(iopt*(S1-X))>=1)Then greek=greek+S1*(Log(S1/S)-r1)
Next i
If igreek=1 Then vg=ert*(greek/S)/nsim
If igreek=3 Then vg=ert*X*tyr*greek/nsim
If igreek=5 Then vg=ert*(greek/sigma)/nsim
QMCOptionGreek135=vg
NIOptionValue函数在一个循环内建立股价运动过程,并计算数值积分的和。注意在循环后两个成份(S和h)是如何插入的。
Function NIOptinValue(iopt,S,X,r,q,tyr,sigma,msd,nint)
’ values option using numerical integration
Dim rnmut,sigt,h,sum,zi,payi
Dim i As integer
rumut=(*sigma^2)*tyr
sigt=sigma*Sqr(tyr)
h=2*msd/nint
sum=0
For i=0 To nint -1
zi=-msd+(i+)*h
payi=(iopt*(Exp(rnmut+zi*sigt)-X/S),0)
sum=sum+payi*(zi,0,1,False)
Next i
NIOptionValue=Exp(-r*tyr)*h*S*sum
End Function
小结
本章以布莱克-舒尔斯欧式期权定价公式为基础,介绍了另外两种用于计算期权价值期望值的方法。
蒙特卡罗模拟从风险中性世界里的股价运动路径中抽样。计算每一条路径的期权收益,然后求其代数平均值,最后用无风险利率折现来对估计期权价值。
与二叉树定价方法相比,蒙特卡罗模拟法得到的估计值误差较大。蒙特卡罗抽样必须生成很多路径,因为与二叉树不同,它的路径不能重组。
可以用减少方差的技术来控制随机抽样,如用对偶变量来减少估计误差。准随机抽样以确定的方式预先设定序列中的数值,它消除了随机变量中的聚集现象。取样的方式就像这些值总是填充在已存在的样本之间。这意味着估计的误差与1/n成比例而不是,其中n为模拟样本个数。
数值积分法是可用于对期权定价的另外一种方法,当期权的收益依赖于多种资产时,这种定价方法尤其有效。
参考文献
Broadie, M. and P. Glasserman, 1996, “Estimating Security Prices Using Simulation”, Management Science, 42(2), 269-285.
Hull, J. C., 2000, “Options, Futures and Other Derivatives”, Prentice Hall, New Jersey.
Moro, B. 1995, “The Full Monte”, Risk, 8(2), 57-58
第13章 非正态分布和隐含波动率
布莱克-舒尔斯期权定价公式假设对数股价收益服从正态分布。首先,再次强调该假设的重要性,即利用对数股价收益正态分布的均值和方差,可以给出布莱克-舒尔斯公式的另外一种表达形式。布莱克-舒尔斯公式也可以用对数正态分布(股价的分布)的前两阶距(monments)表示。
应用布莱克-舒尔斯公式时,除了期权有效期内股票收益的波动率外,其它参数都是已知的。给定波动率一个具体数值,通过公式得出一个期权价值,同样,对该过程可以进行反向计算。即,给定一个期权价格的市场观测值,可以计算布莱克-舒尔斯公式中的隐含波动率。找隐含波动率(或者ISD,表示隐含标准差)的过程可以通过手工反复试验得出,改进的方法就是让该试验过程自动化。这里将讨论确定初值的不同方法,以及通过牛顿-拉夫森迭代寻找一个较佳的ISD(隐含标准差)估计值。
理论界常常对期权定价中怎样允许偏离严格正态分布的情况比较感兴趣。我们在此讨论两种修正,一种是可选择的分析公式,另一种是可选择的二叉树。第一种情况通常设定股价服从一个逆gamma(RG)分布,而不是常见的对数正态分布;第二种情况保持了股价的对数正态分布,但允许对数收益的高阶矩(偏度和峰度)与严格正态分布有差异。试验表明,对数股价收益是典型的肥尾(峰度大于3)和偏度现象。
本章最后介绍由期权市场价格产生的隐含波动率曲线,它用于反映对数收益分布在不同假设下的波动情况, 而不是用于预测波动率。
除了赫尔(第17章)关于波动率曲线的讨论外,标准文献并不没有本章所讨论的专题。更详细的内容需要查阅相关的专业论文。然而,工作簿中的所有计算过程都编成了用户定义函数程序。
非正态分布假设下的布莱克-舒尔斯 公式
在强调股价收益分布假设的前提下,中表Dist描述了其它分布条件下布莱克-舒尔斯期权定价过程。首先,强调对数股价满足正态分布,可以计算出均值和方差,并用这两种矩重新表述布莱克-舒尔斯公式。
由于,,故能够简写成,因此关于欧式看涨期权的布莱克-舒尔斯公式可以写成:
符号表示公式中使用了分布的矩。
在图中,对数股价正态分布的矩分别在E4(M)和E5(V)中,其中,M的计算公式为=LN(B4)+(*B12^2)*B11,关于V的计算公式为=(B12^2)*B11。E13中用矩表达式得出的期权价格为,和布莱克-舒尔斯公式所得值(B17)完全一致。
Option Values using the LN and RG distributions:
对数正态分布和逆gamma分布条件下的布莱克-舒尔斯定价
【参照书中第210页的图】
图 对数正态分布或逆gamma分布条件下的布莱克-舒尔斯定价
定价公式(对数正态情况下的布莱克-舒尔斯公式)同样能表示成股价对数正态分布的矩形式,即:
和能分别通过M1和M2的形式表述,对数正态分布的矩(M1在H4中,M2在H5中)能通过正态分布的矩M,V计算得到:
M1=exp(M+), M2=exp(2M+2V)
关于矩的计算公式见节。H17中得到的期权期权价值利用M1和M2作为用户定义函数LNOptionValue(相关代码见)的输入参数,在图中,LN value值为,与布莱克-舒尔斯值完全一致,尽管输入参数不一样。
由矩M1和M2出发,可以给出另外一种期权定价方法,该期权定价方法用股票价格服从RG分布(逆gamm)来替代对数正态分布的假设。Milevsky和波斯纳(1998)提出,此分布适合于对所谓篮子期权(basket options)的定价,在此,将其用于普通期权定价。
称随机变量Z服从逆gamma分布是指它的倒数(1/Z)服从gamma分布。可以用逆gamma分布代替正态分布[N(d)]对期权定价。gmma分布依赖期权执行价的倒数(1/X)和另外两个参数alpha(H7)和beta(H8)。 输入参数可以用M1和M2计算得到。
RG看涨期权定价公式为:
其中,g1和g2取代了正态分布函数和。在图中,由RG定价公式给出的值为(H3),于布莱克-舒尔斯定价值相比。RG和布莱克-舒尔斯定价之间的差距只有在期权出现虚值(out of the money),即S相对较小时才会比较明显。为了验证这点,可以将B4中的股价由100降到75,并注意得出的期权价格。这里,B17,H17,E20和F20值都需要重新计算,每次计算可通过F2和回车键实现。
隐含波动率(Implied Volatility)
下面,利用布莱克-舒尔斯公式估计市场中不同期权价格的隐含波动率。简单依靠反复试验就可以得到与观察期权价格相匹配的隐含波动率。有多种不同的实现方法:Excel的单变量求解,科拉多和米勒(1996)近似,马纳斯特和凯勒法(1982)或用户定义函数,分别介绍如下。
图中,标准看涨期权的详细信息都已列出(B4到B12),由用户定义函数BSOptionValue计算的期权价值为。假设观测到的该期权市场价值为, 接下来的任务是通过一系列B12的估计值找到一个与布莱克-舒尔斯定价为相匹配的隐含波动率。可以手工完成,也可以通过Excel菜单工具中的单变量求解完成,用单变量求解命令时,目标单元格为B15,目标值为15,可变单元格为B12,解得实际的隐含波动率为%。
以上的单变量求解法可以用一个用户定义函数BSOptionISDGoalSeekNR来复制。该函数记下每次波动率估计值的当前误差(运用该估计值得到的期权价格与实际价格的差)和定价函数相对于波动率的斜率。定价函数相对于波动率的斜率(一阶倒数dc/d)与vega的作用相同(在中讨论的一个期权希腊参数)。使用斜率来提高后续猜测的精确性,此方法被称为牛顿-拉夫森方法。在一定的精度要求下,很容易由一个循环完成牛顿-拉夫森方法的编程,直至得到期权价格的观测值 。BSOptionISDGoalSeekNR的具体代码将在节中介绍。
Estimating Implied Volatility(or ISD) in BS Option Values:
在BS定价过程中估计隐含波动率(或隐含标准差)
【参照书中第212页的图】
图 通过市场价格与BS定价结果相匹配来估计隐含波动率
科拉多和米勒(1996)给出了一个用于近似计算隐含波动率的解析公式。首先,把N(d)近似地表示成线形函数,并将这个近似的线形函数代入布莱克-舒尔斯公式,与看涨期权价格观测值一起得到一个二次方程式。在保持布莱克-舒尔斯公式输入参数和观测看涨期权价格的情况下,解上述方程即可得到一个关于隐含波动率的近似值。我们用科拉多和米勒方法解的近似值作为BSOptionISDGoalSeekNR函数中牛顿-拉夫森迭代的初试值。
在期权执行价格和股票当前价格比较接近的情况下,这种估计是比较精确的。如果执行价为95,期权观测值为15,由科拉多和米勒法得出的值为%(H4),同样情况下,由BSOptionISDGoalSeekNR公式得到的结果为%。然而,对于一些极端的情况,这种线形近似的方法将失去作用。例中,设置B12等于20%,观测期权价格B17为。calc1的值(科拉多和米勒法的一个中间结果)在这种情况下为负(单元格H9),该近似法无效。
对于这种情况,马纳斯特和凯勒(1982)提出了一种方法,用于给单变量求解(Goal Seek提供一个有效的初值。这些初值(称为种子值)是这样选择的,要满足或等于,并指出,在有解的条件下,从这个种子值开始计算一定能得到正确的隐含波动率。对于上述科拉多和米勒法失效的情况(观测的期权价格为),可以使用%作为马纳斯特和凯勒的种子值,用此作为初值,通过牛顿-拉夫森迭代,可以产生连续改进的隐含波动率估计值,最终达到正确的结果%。
调整偏度(Skewness)和峰度(Kurtosis)
研究人员特别关心对数收益比布莱克-舒尔斯公式中假设正态分布具有更大的高阶矩时的期权定价问题。鲁宾斯坦(1998)给出怎样调整二叉树,使其产生给定偏度和峰度的分布。该方法实现起来分两步,这里用图(Edge表的一部分)中关于欧式期权的例子来说明。首先,利用所谓的埃奇沃思因子产生一个离散分布(调整后的二项分布),此分布具有给定的偏度和峰度。第二步,利用股价分布(调整后的二项分布)和埃奇沃思概率计算期权收益的期望值。
Generating an Edgeworth distribution:
生成一个埃奇沃思分布
【参照书中第213页的图】
图 在给定偏度和峰度条件下产生埃奇沃思分布
在工作簿的表JRBinomial中,我们说明了怎样用一个离散的二叉树来近似一个连续的标准正态分布;同样,在JREuro中说明期权价值是一个离散的期望值,此期望值由期权在各结点的收益与相应的概率值加权得到。
如图所示,埃奇沃思分布由一些列节点与相应的概率构成。由于这里选择的是16期二叉树模型,因此初始的节点范围为小于均值4倍标准差到大于均值的4倍标准差,即区间[均值-4*标准差,均值+4*标准差]。用二叉树节点的分布概率(D列,实际上是n=16,p=的二项分布的概率)乘以埃奇沃思扩展式(E列)即可得到调节后的概率分布(F列)。将F列调整为概率之和为1的H列。这时,埃奇沃思分布有3阶(H6)和4阶矩(H7),它们的值应该接近于给定的B6和B7中的值。最后,为了保证埃奇沃思分布均值为0,标准差为1,将B列中的初始节点值变换为J列值。以上完成了鲁宾斯坦方法的第一步。
对于一个峰度偏大的对称分布(在此,偏度为,峰度为),埃奇沃思调节增大了两端情况(大于均值2个标准差)的概率。图为效果图。由于将中间概率的一部分调整给了两端,相对于布莱克-舒尔斯定价,这里得到的期权价值会底些。
Edgeworth distribution():Edgeworth分布与标准正态分布的比较
Standard deviations from mean:标准差
【参照书中第214页的图】
图峰度为的埃奇沃思分布与标准正态分布的比较
到此,我们一直在使用均值为0,方差为1的标准分布。还需要对它进行调整,使得股价分布具有适当的风险中性均值和方差。当对数股价服从正态分布,按年计算均值的漂流量为,在此等于(见图的单元格D44)。由于与严格正态分布存在一定的偏差,因此,将单元格D45计算的值称为 埃奇沃思风险中性漂移或者RNdrift E。
下面,进行期权定价的第二步计算,见图,将第一步计算的节点复制到单元格D49到D65,第一步计算的概率仍然在单元格H49到H65。股票价格的离散分布在E列,相关看涨期权收益在F列,使用埃奇沃思分布计算得到期望值在J列。最终得到的看涨期权价值为(单元格J35),正如前面所预料那样,小于布莱克-舒尔斯 公式的结果。
在严格正态分布的假设下检验一下前面的结果,设定偏度值(B6)为,峰度值(B7)为,那么由埃奇沃思得以的期权价值将非常接近布莱克-舒尔斯公式所得值。微小差别是由于在定价模型中仅仅使用了16期二叉树来近似连续的对数正态分布引起的。
Valuing an Option based on Edgeworth Distribution:
基于埃奇沃思分布的期权定价
【参照书中第215页的图】
图 Edge表中对股价服从埃奇沃思分布的看涨期权进行定价
由于篇幅原因,这里仅用一个16期的二叉树来说明埃奇沃思期权定价方法。鲁宾斯坦建议,为了达到精确结果,至少需要100期。将期数定义为输入参数,使用用户定义函数EdgeworthEuroOption,很容易对期权进行定价。该函数的使用见单元J36。
波动率曲线(The Volatility Smile)
如果将埃奇沃思分布定价法得到的看涨期权价值与布莱克-舒尔斯定价法得到的价值进行比较,其结果可以用于说明所谓的“波动率曲线”。波动率曲线是指期权的隐含波动率与执行价格的函数图。如果布莱克-舒尔斯公式中关于分布的假定完全成立,那么,观测到的波动率曲线应该是一条直线。工作簿中的表Smile是对该方法的说明,图为Smile表的部分内容。
上一节中,利用16期埃奇沃思定价法,在峰度偏大,波动率为20%的情况下得到看涨期权定价为。定价结果存于表Smile的单元格J10中,取名EDGE(S,X,)用以标识定价方法和参数。如图中的单元格J13所示,在波动率为%的情况下,布莱克-舒尔斯公式得到事实上价结果也是。此隐含波动率(即%,标识为EDGE ISD)是在将期权价格设定为埃奇沃思定价结果值的条件下,由函数BSOptionISDGoalSeekNR计算得到的(单元格J12)。
因此,两种不同的假设条件得到了相同的定价结果:服从严格正态分布股价波动率为%,或对数收益的峰度偏大时,相应的股价波动率为%。因此,期权观测值的差异不仅仅反映了未来波动率估计的差异程度,更多反映了对数股价收益偏离严格正态分布的程度。
Estimating Implied Volatility( or ISD) in Edgeworth Option Values:
在埃奇沃思期权定价过程中估计隐含波动率(或隐含标准差)
【参照书中第216页的图】
图 表Smile中对数正态和非对数正态条件下的看涨期权价值
对于一定范围执行价(见图,列E),重复计算埃奇沃思期权价值和隐含波动率,就可以得到在给定偏度和峰度条件下的波动率曲线。选择平值执行价,即选择以X的折现值(单元格B22中的)为中心。选择范围为期权平值执行价左右倍标准差的区间。
Illustrating the Volatility Smile:演示波动率曲线
【参照书中第216页的图】
图 BS和埃奇沃思假设条件下的波动率比较
利用埃奇沃思分布计算不同执行价格下的看涨期权价值(列G),相应的隐含波动率通过函数BSOptionISDGoalSeekNR求得(列J)。图即为隐含波动率与执行价格的函数图,即波动率曲线。
Implied volatility smiles:隐含波动率曲线
Implied volatility:隐含波动率
Exercise price:执行价格
【参照书中第217页的图】
图 表Smile中的波动率曲线
如图所示,通过选择适当的偏度和峰度参数值,利用埃奇沃思期权定价法,就能画出如图所示的波动率曲线图。选择不同的参数值,利用埃奇沃思期权定价法可以画出不同形状的波动率曲线图。
Module1中的用户定义函数
这里有两个函数,强调了布莱克-舒尔斯公式中假设股票收益服从对数正态分布的重要性。第一个函数名为LNOptionValue0,使用正态分布的矩。第二个函数名为LNOptionValue,使用对数正态分布的矩。
Function LNOptionValue 0(iopt,M,V,X,r,tyr)
’ returns lognormal option value(iopt=1 for call,-1 for put)
Dim d1,d2,Nd1,Nd2
d2=(M-Log(X))/Sqr(V)
d1=d2+Sqr(V)
Nd1=(iopt*d1)
Nd2=(iopt*d2)
LNOptionValue0=Exp(-r*tyr)*iopt*(Exp(M+*V)*Nd1-X*Nd2)
End function
Function LNOptionValue(iopt,M1,M2,r,tyr)
’ returns lognormal option value(iopt=1 for call,-1 for put)
’ uses LNOptionValue0 fn
Dim M,V
M=2*Log(M1)*Log(M2)
V=Log(M2)-2*Log(M1)
LNOptionValue=LNOptionValue0(iopt,M,V,X,tyr)
End Function
RGOptionValue函数在结构上与上述函数相同,最大的差别就是用逆gamma分布函数代替了对数正态分布(程序中使用到Excel的GAMMDIST函数,输入参数TRUE时返回累积概率值)
Function RGOptionValue 0(iopt,M1,M2,X,r,tyr)
’ returns reciprocal gamma option value(iopt=1 for call,-1 for put)
Dim alpha,beta,g1,g2
alpha=(2*M2-M1^2)/(M2-M1^2)
beta=(M2-M1^2)/(M1*m2)
g1=(1/X,alpha-1,beta,True)
g2= (1/X,alpha,beta,True)
if iopt=-1 then g2=1-g2
RGOptionValue=Exp(-r*tyr)*iopt*(M1*g1-X*g2)
End Function
BSOptionISDGoalSeekNR函数使用科拉多和米勒估计值作为初始点进行牛顿-拉夫森迭代(下面程序中用sigmanow表示)。当科拉多和米勒方法失效(sigmanow=-1)时,使用马纳斯特和凯勒给出的种子值进行计算:
atol=
sigmanow=BSOptionSDEstimate(iopt,S,X,r,q,tyr,optprice)
’ when above fails,start from Manaster & Koehler seed value
if sigmanow<=0 Then sigmanow=Sqr(2*Abs(Log(S/X)+(r-q)*tyr)/tyr)
Do
fval=BSOptionValue(iopt,S,X,r,q,tyr,sigmanow)-optprice
fdashval= BSOptionValue(iopt,S,X,r,q,tyr,sigmanow)
sigmanow=sigmanow-(fval/fdashval)
Loop While Abs(fval)>atoll
BSOptionISDGoalSeekNR=sigmanow
函数EdgeworthEuroOptionValue相对于表Edge来说是一个比较综合的函数,附加了对概率密度函数的非负(由PDFnonneg标识)和单众数(由PDFmodes标识)检验。不满足时,用格拉姆-查利展开替代埃奇沃思展开。如果检验仍不能通过,就返回值-1。
For j=1 To n
xvec(j)=(2*(j-1)-nstep)/Sqr(nstep)
bvec(j)=(nstep,j-1)*(()^nstep)
Next j
’ Edgeworth expansion used for pdf
For j=1 To n
xj=xvec(j)
c=1+skewco*(xj^3-3*xj)/6+(kurtco-3)*(xj^4-6*xj^2+3)/24
c=c+skewco^2*(xj^6-15*xj^4+45*xj^2-15)/72
fvec(j)=c*bvec(j)
Next j
it1=PDFnonneg(fvec)
it2=PDFmodes(fvec)
if it1<0 Or it2> Then
’ use Gram-Charlier expansion for pdf instead
For j=1 To n
xj=xvec(j)
c=1+skewco*(xj^3-3*xj)/6+(kurtco-3)*(xj^4-6*xj^2+3)/24
fvec(j)=c*bvec(j)
Next j
it1=PDFnonneg(fvec)
it2=PDFmodes(fvec)
End if
if it1=<0 Or it2> Or kurtco<3 Then
’ method fails as pdf has non-negative entries or is not unimodal
ve=-1
Else
’ minor adjustments to fvec and xvec
frvec=PDFrescale(fvec)
xsvec=Xvecstd(xvec,frvec)
另外,还要调整密度函数,并将期权收益期望的折现值作为计算出的二叉树期权价值。鲁宾斯坦指出二叉树期数应在100以上才能满足期权定价需要。
’ now start option value
sum=0
For j=1 To n
sum=sum +frvec(j)*Exp(xsvec(j)*sigma*Sqr(tyr))
Next j
’ now calculate mmu,risk-neutral expectation of ln(Sj/s)
mmu=r-q-(Log(sum))/tyr
ve=0
’ option valu depengs on share price (Sj) and risk neutral pdf(frvec)
For j=1 To n
Sj=s*Exp(mmu*tyr+xsvec(j)*sigma*Sqr(tyr))
ve=ve+frvec(j)*(iopt*(Sj-X),0)
Next j
ve=Exp(-r*tyr)*ve
End if
EdgeworthEuroOptionValue=ve
小结
在布莱克-舒尔斯期权定价公式中,只有波动率是不能直接测量的。然而,我们可以计算隐含波动率,即,利用该公式得出期权市场观测价格时的隐含波动率。可以使用Excel提供的单变量求解工具,但更好的方法是直接利用这里给出的用户定义函数。
布莱克-舒尔斯公式假设股票价格服从对数正态分布,事实上,实证研究表明,该分布存在厚尾和不对称性(即,存在过大的峰度和偏度)现象。鲁宾斯坦给出一种方法,即调整二叉树法,使之产生具有给定偏度和峰度的分布。用此方法,可以产生不同的关于股价树的离散概率分布(埃奇沃思分布),且前4阶矩相匹配。
虽然正态分布是期权定价的基础,但期权交易中仍存在偏离正态的情况。因此,有必要绘制波动率曲线图。当股票价格不服从对数正态分布时,隐含波动率将偏离由布莱克-舒尔斯公式给出的值。将波动率作为期权执行价格函数进行作图,得到的就是波动率曲线图。
参考文献
Corrado, C. J. and T. W. Miller, 1996, “A Note on a Simple, Accurate Formula to Compute Implied Standard Deviations”, Journal of Banking and Finance, 20, 595-603.
Manaster, S. and G. Koehler, 1982, “The Calculation of Implied Variances from the Black-Scholes Model”, journal of Finance, 37(1), 227-230.
Milevsky, M. A. and S. E. Posner, 1998, “Asian Options: The Sum of Lognormals and the Reciprocal Gamma Distribution”, Journal of Financial and Quantitative Analysis, 33(3), 409-422.
Rubinsten, M., 1998, “Edgeworth Binomial Trees”, Journal of Derivatives, 5(3), 20-27, (also see correcton: 5(4), 6).
第14章 债券期权定价介绍
由于要处理利率期限结构问题,债券期权定价要比股票衍生证券定价复杂。利率期限结构描述了利率与债券到期期限之间的关系。利率期限结构是根据市场上不同到期期限债券的价格估计出来的。这个估计过程比较复杂,因为大部分债券含有一系列的利息支付(一般是每年两次)并在到期时支付本金。但是也有一部分债券在发行期间不支付票息,这就是我们熟知的零息票债券。
在为债券期权定价时,有三种处理利率期限结构的方法:(i)忽略期限结构;(ii)对期限结构进行建模;(iii)拟合期限结构。债券期权定价从尝试采用布莱克-舒尔斯公式(第一种方法)发展到连续利率模型(第二种方法),现在更受关注的是拟合期限结构的离散利率模型(第三种方法)。
本章内容分为三部分,首先讨论利率期限结构以及如何选择适当的贴现因子为附息债券现金流定价,然后介绍如何利用一个简单的利率二叉树来模拟零息票债券的价格,最后介绍布莱克债券期权定价公式,此公式忽略利率期限结构,并假设债券未来的价格服从对数正态分布。
第15章介绍利率模型,主要是Vasicek与考克斯,英格索尔和罗斯(CIR)的瞬时利率模型。这些随机模型产生一组利率,根据这些利率就可以为不同到期期限的零息票债券定价。这些模型的优点是它们给出了零息票债券期权价值的解析解,由此可以为附息债券期权定价。
最简单的形式Vasicek模型和CIR模型就是一个可能的期限结构,但是这个期限结构往往跟市场中能观察的期限结构不一致。第16章有关拟合期限结构方面的内容展示了如何利用二叉树对利率分布进行建模,从而正确地给零息票债券定价(也搭建了波动率的期限结构)。构建了两个简单的二叉树,其中一个假设利率服从对数正态分布,而另外一个假设服从正态分布。构建一个简单的布莱克,德曼和托伊(BDT)树。BDT树是在简单的对数正态树的基础上发展而来,并广受从业者的欢迎,因此可以用它来说明零息票债券期权的定价。
尽管为债券期权定价是一个复杂的过程,但刚开始应该熟悉期权定价。定价的过程中有两个重要的部分:根据期权在执行时的收入计算风险中性期望值以及使用数值方法(例如二叉树)。
为了使说明更简单,这里将期限结构模型局限于单因子模型,并只采用市场上可观测的零息票债券收益率和波动率。除了在BDT树的例子中引入离散的利息支付以外,一般假设利息支付是连续的。
这些模型可以在Excel文件BOND1和BOND2中实现。所用到的Excel技巧大部分都是我们熟悉的,同时还会在附息债券期权定价和构造利率树的过程中使用单变量求解。由于大部分公式都比较复杂,因此使用用户定义函数来实现会更简单有效,读者可以在工作簿的Module表中找到这些用户定义函数。
相关内容可参见Bodie等人(1996),关于第13,14章可以阅读《债券价格和收益》以及《利率期限结构》等书籍,还可以参考赫尔(2000)的文章以及《利率衍生品》第21章。另外一篇期权的参考文章是克卢洛和斯特里克兰(1998)。书中还有部分较新的内容,包括利用CIR模型为债券期权定价(涉及到非中心卡方分布)。
利率期限结构
图列出了到期期限从一年到十年的零息票债券价格,读者可以在BOND文件中的Intro工作表找到这些价格信息。
Term Structure of Interest Rates:利率期限结构
【书中224页的图】
图 零息票债券价格,相应的到期收益率和远期利率
零息票债券价格可以转换为相应的零息票债券的连续复利收益率,它们之间有以下的关系:
其中是零息票债券价格,是债券的到期期限,债券的面值为1,则是相应的零息收益率。(这里用零息收益率代表零息票债券价格暗含的各个期限的利率水平,从0时刻到t时刻。)单元格公式在F5单元格中。一系列不同到期期限的收益率组成了所谓的利率期限结构。例如,一年期的零息收益率为%,而10年期的零息收益率为%。与一年期的零息收益率不同,其它的零息收益率跨越了多个时期。
期限结构中的信息也可以用一系列的远期利率来表示,如H列所示。H6单元格中 %的远期利率表示暗含的第二年的借债利率(一年期),它是根据一年期和两年起的零息票债券价格算出来的。零息收益率和远期利率的关系展示在图中(见工作簿的图表1)。这样,利率期限结构中的信息可以通过三种途径来显示:利用零息票债券价格,零息收益率和远期利率。
Term Structure of Interest Rates:利率期限结构
Maturity:到期日
【书中225页的图】
图 零息票债券价格暗含的零息收益率和远期利率(图1)
附息债券的现金流和到期收益率
大部分交易的债券都有规范的利息支付和到期的本金支付。例如,图列出了面值为1,10年后到期,票面利率为5%的债券的现金流(在Intro工作表的H25:H34单元格中)。
Valuing Coupon Bonds:附息债券定价
【书中225页的图】
图 根据零息票债券价格给附息债券定价并计算到期收益率
利用零息票债券价格(D列)可计算现金流的现值,而债券的现值是这些单个现金流现值的简单加总(在F列)。总和是(在F20单元格),这一数值应该等于债券的市场价格。
根据市场价格(H24中的初始现金支出)和一系列的现金流入(如H列所示),可以直接计算附息债券的到期收益率。到期收益率的定义是所有债券现金流(包括初始现金流出)的内部收益率。它可以用Excel中的IRR函数直接计算。实际上,IRR函数返回离散情况下的债券利率收益率。这个收益率可以转换为连续复利收益率,如单元格H20中所列的公式:
在J列,债券的现金流都是用到期收益率进行折现的,如单元格J20所示,总和仍然是。
计算到期收益率并不复杂。但问题是,它是否适合于为债券的现金流定价。当使用到期收益率的时候,假设不同时刻收到的现金支付都可以用同一个利率进行折现。而且,对于市场上不同的附息债券,会估计出不同的到期收益率(尽管它们的到期期限一样)。因此,最好使用随期限改变而改变的利率,而这就是所谓的利率期限结构。
二叉树
由于债券定价非常依赖于不确定的利率,因此就需要能够描述利率未来状况的模型。和在股票期权定价过程中使用二叉树描述股票价格的随机性质一样,可以构造二叉树模型来描述利率的不确定性。债券期权可以通过利率树来定价。该方法可由图说明。利率树中的短期利率(B46:E49单元格)反映了零息收益率在未来四年的情况。第一个时期的利率为%(即一年期债券的零息收益率),在第二个时期,利率被假设为%或者%,出现这两种情况的概率相等。如此类推。目标是使得四年期零息票债券的理论价格(A52单元格)与四年期零息票债券的市场价格(D8单元格)对应起来。
在这个简化的例子当中,二叉树中的短期利率被假设是已知的,在同一时点以相同的概率向上或者向下变动(B39和B40单元格)。与股票期权定价的过程相似,这些利率也可以为现金流定价。(第16章将讨论这些利率如何包含了零息收益率波动的假设。这里假设这部分分析过程已经完成。)
在第四年年末,四年期零息票债券现金流的价值等于它的面值,无论到时的短期利率是多少。最终的现金流(在单元格E52到E56中,对应着每一个路径)在二叉树相应路径的利率下,被折现回原点。
例如,折现到前一个时期,计算公式在单元格D52中:这里利用了一个依赖于相应单元格所示利率的折现率(E46)而不像在股票期权定价中使用固定的折现率。复制这个公式,顺着二叉树往前倒推,就得到四年期零息票债券的价格。
Valuing Zero-Coupon Bonds using Binomial Trees:
利用二叉树为零息票债券定价
【书中227页的图】
图 利用短期利率的二叉树为零息票债券定价(参见Intro工作表)
这个例子的关键点在于:可以构建一个能与期限结构(这里表现为零息收益率)对应的利率二叉树。
布莱克的债券期权定价公式
第一个对零息票债券期权进行定价的方法是由布莱克(1976)提出的。在期货期权定价的章节中(节)已经提到过这个公式。布莱克假设在期权到期时,这期债券价格服从对数正态分布。这使得修正的布莱克-舒尔斯公式可以应用。
在图中,布莱克公式用于为一个四年期的期权(T=4)定价,期权的标的物是面值为1,剩余到期期限为10年(s=10)的零息票债券。在单元格B64中,债券未来的理论价格是用面值乘上10年期与4年期零息票债券价格的比值得到的,单元格中的公式是:
=B61*(B63/B62)。
期权的执行价格为,短期利率假设为6%,其波动率假设为3%。和的公式与期货期权中的公式是一致的,这样可以计算得到看涨期权的价值为。
这里所说明的布莱克公式只是债券期权定价的一种最简单的方法,并且它只适用于执行期限相对于债券到期期限比较短的情况。接下的两章中,将会介绍更多债券期权定价的复杂方法。
Valuing European Options on Zero-Coupon Bonds:
零息票债券的欧式期权定价
【书中228页的第一个图】
图 Intro工作表中的布莱克零息票债券期权定价方法
久期和凸性
零息票债券把债券价值和到期收益率联系起来,因为对于这种特殊的情况,债券的到期期限与债券现金流的重心恰好对应。久期,即现金流(利息支付和本金支付)现值的加权平均,代表了债券现金流的重心位置(以年来表示)。对于附息债券来说,债券价值和到期收益率之间再没有直接的联系。但是,可以通过久期(这里作斜率或者价值与收益率的一阶导数)和凸性(看作曲率或者价值和收益率的二阶导数)去衡量由于债券收益率变化一个单位所引起的债券价值发生的变化程度。
Calculating Duration of Coupon Bonds:计算附息债券的久期
【书中228页第二个图】
图 计算久期
但是有一点要提醒读者的是,这种分析假设利率期限结构是平行移动的,但实际的情况往往不是这样的,这也是这种估计方法的重要缺陷。尽管存在这样的问题,但是久期和凸性仍然被从业人员广泛使用,这也是本章介绍它们的原因。例如,债券的到期期限为10年,票面利率为5%,现在的到期收益率为%(离散利息支付),其计算列在表中。麦考利(Macaulay)久期的计算是单个现金流现值(在I91单元格中)的总和除以债券的现值(H91单元格中)。也可以通过VBA用户定义函数中Chua的债券久期公式Chua Bond Duration (Chua,1984)进行计算。而从业人员一般使用修正久期,即麦考利久期除以1与到期收益率的和。当参数imod取1时Chua Bond Duration函数返回修正久期的值(见B95单元格)。
下面,我们利用修正久期(B95单元格)和凸性(B97单元格)说明如何估算当到期收益率发生一定变化时债券价值所发生的变化,见表。债券凸性的计算比久期要复杂,但是可以通过用户定义函数Black Orszag Convexity(布莱克和Orszag,1996)进行计算。B101单元格中的公式假设债券价值变化的百分比等于到期收益率的变化乘上修正久期的负数后,再加上到期收益率变化的平方与凸性一半的乘积。如果到期收益率增加(B99单元格),债券价格会下降%,到(单元格H110,即到期收益率变化为%后债券现金流的现值之和)。这样,当假设收益率曲线发生平行移动的情况下,久期和凸性能够很好的估计债券价值发生的变化。
Using Duration and Convexiry to Estimate Impact of Change in YTM:
利用久期和凸性来估计到期收益率变化的影响
【书中229页的图】
图 估计收益率变化对债券价值的影响
符号
到目前为止,在Intro工作表中介绍的简单模型并不需要新的符号。但是,在第15章和BOND1文件的其它工作表中需要引入简化的符号。特别是涉及到债券到期期限和债券期权执行期限的时间结构时,更需要引入符号。
一般地,假设现在是0时刻,即t=0,而期权执行的时刻为t=T,债券到期时刻为t=s。
代表s时刻到期的零息票债券在0时刻的价格(在s时刻支付1元),也可以称作零息票债券价格。注意,也可以看作一个折现因子,这个折现因子等于,假设利息是连续支付并且利率不变。
表示从0时刻到s时刻的零息票债券收益率。没有特别说明的情况下,这个利率一般为连续复利年利率。
小结
本章中,用‘收益率’这个词描述了一个时间段的年利率。还通过例子说明如何根据零息票债券的市场价格来计算其收益率。这些收益率又称为‘零息收益率’,为利率期限结构估计值。
通常,债券是有中间利息支付的,而它们现在的市场价格反映了预期现金流(包括每期的利息支付和最后的本金)的价值。过去,现金流的到期收益率(内部收益率)被看作是一种定价的手段。但是在现在看来,现金流的折现率应该使用零息收益率而不是带有武断性质的到期收益率。
最早的债券期权定价方法是布莱克提出的,这个方法中使用的公式中含有债券未来的价格,是在原来布莱克-舒尔斯公式的基础上扩展而来的。然而,这种方法暗含的假设是不倾向于持有债券,特别是在某些限制的情况下。
为了给第16章做准备,我们展示了利率二叉树如何对应一个给定的期限结构。假设这样的利率树可以建立,那么它们就是我们所熟悉的数值方法,从而可以为债券期权定价。
参考文献
Black, F., 1976, “The Pricing of Commodity Contracts”, Journal of Financial Economics, 3, 167-179.
Blake, D. and J. M. Orszag, 1996, “A Closed-Form Formula for Calculating Bond Convexity”, Journal of Fixed Income, June, 88-91.
Bodie, Z., A. Kane and A. Marcus, 1996, Investments, 3rd edition, Richard D. Irwin, Englewood Cliffs, Nj. Chua, J. H., 1984, “A Closed-Form formula for Calculating Bond Duration”, Financial Analysts Journal, May/June, 76-78.
Clewlow, L. and C. Strickland, 1998, Implementing Derivatives Models, John Wiley & Sons, Chichester.
Hull, J. C., 2000, Options, Futures and Other Derivatives, Prentice Hall, New Jersey.
第15章 利率模型
本章主要内容是通过利率模型给零息票债券定价。此方法假定,短期利率的变化可以通过随机模型来描述,此随机模型产生一个零息票债券价格的期限结构。通过利率模型,可以得到一个零息票债券价格的解析解。Jamshidian(1989)给出了零息票债券价格如何被用于为零息票债券期权定价,从而进一步为附息债券期权定价。
本章将重点放在两个主流模型上,分别是Vasicek(1977)模型与考克斯,英格索尔和罗斯(CIR;1985)模型。这两个模型都假设(瞬时)短期利率r的风险中性过程是随机的,并只有一个不确定性来源(单因子)。随机过程包括漂移和波动率两个参数,它们只与短期利率r有关,与时间无关。短期利率模型包括好几个参数,不同参数组合能产生不同的利率期限结构形状。
两个利率模型都有所谓的均值回复性质,即短期利率有回复到潜在利率水平的趋势。这个性质是根据市场利率变动形式归纳出来的。两个模型的不同之处在于对波动率的方法上。先从Vasicek模型开始,然后考虑CIR模型。BOND1文件中的电子表格(Vasicek和CIR工作表)有相同的格式,使用同一数值例子。
Vasicek利率期限结构模型
Vasicek模型中,短期利率r的变动为以下形式的随机过程:
这样,在时间增量过程中,短期利率的微小变动以的速率回复到均值水平。第二项波动项包含了不确定性,代表了一个正态分布,期均值为0,方差是。短期利率(严格来说是)被假设为t时刻的连续复利瞬时利率。
在给股票期权定价时,基本的方法就是获得风险中性世界中期权未来收益的期望折现值。同样的,股票现在的价格可以对等的看作风险中性世界中它未来价值的期望折现值。将同样的原则应用到债券和债券期权未来的收益上。在一个零息票债券期权的例子中,在s时刻等于1元的价值,其在t时刻的对应值可以表示为:
这里积分的时间间隔是从下限t到上限s,符号表示风险中性期望。
对于股票期权来说,利率被假设为固定的,因此短期利率积分一项可以拿到期望括号的外边。这样,上式就变为一个折现因子。因此,对于股票期权来说,定价决定于计算未来收益的风险中性期望值,而可以作为一个固定的折现因子。
对于零息票债券期权来说,模型描述的短期利率公式必须包含在的期望表达式中。由于短期利率的不确定性,因此它不能被提到期望括号的外边。
在Vasicek模型假定下,可以解出积分的解析解,从而给出零息票债券的价格,即:
这里是短期利率在t时刻的值
在的特殊情况中,A和B的表达式可以简化为:
和
这些数值在电子表格中都很容易计算。表为从BOND1文件的Vasicek工作表中摘录了部分内容。
Vasicek Model:Vasicek模型
【书中232页的图】
图 零息票债券价格,零息收益率和Vasicek模型中的波动率
Vasicek模型中要求短期利率r的三个参数(,和)必须根据历史数据估算出来。在上面例子中,所使用的参数,和是由Chan等人在1992年根据1964年到1989年的美国一月期国库券收益率数据估算出来的。
在图的例子中,短期利率的初始值为6%,而潜在的利率回复水平为(%),列在B6单元格中。模型可以给出相应的零息票债券的价格,从现在到10年末的任意一个时刻。单元格B13和B14分别是和的公式,根据这两个公式,到期期限为s的零息票债券的几个就可以计算出来(列在E6单元格中)。这说明通过Vasicek模型的计算,在s时刻(现在s=10年)收到的每单位价值的现值为。根据零息票债券的价格,可以计算出零息收益率,这里是%。
一个无限期债券的到期收益率,这里用表示(E14单元格),为%。这个数值比所选的参数(短期利率均值回复水平)更小。这个短期利率波动率的公式列在单元格E17中,表明10年期零息收益率的波动率为%,而我们选择的波动率参数为2%。
可以建立一个模拟运算表,计算不同的s值(从0到30)所对应的零息收益率和它的波动率。表中所列出的零息收益率代表利率期限结构,对应图中最下方的曲线(也可参见工作簿的图2)。
Vasicek-Possible Shapes for Term Structure: Vasicek-利率期限的可能结构
Bond Maturity:债券到期日
【书中233页的图】
图 不同的r值所对应的利率期限结构
如图所示,零息收益率的期限结构可以出现三种可能的形状,主要依赖于B7单元格中短期利率的现值。可能出现的形状包括单调上升(当r比小时),驼峰和单调下降(当r比b大时)。可以通过改变工作簿中r的值来检验以上的关系。例如,当调整B7的值为9%时,这个图形会显示单调下降的期限结构,而将B7改为%时,会产生一个驼峰状的期限结构。
Vasicek利率模型的一个缺陷是有时候它会产生负的利率。见图中显示的例子,此图是B5单元格情况下Vasicek模型模拟的情况。这个问题在CIR模型中不会出现。但是,在转入CIR模型之前,先来看看如何利用Vasicek模型为零息票债券期权定价。
Short rate:短期利率
【书中234页的图】
图 Vasicek模型模拟的短期利率
Vasicek模型对零息票债券欧式期权定价
Vasicek工作表中的零息票债券期权定价公式由Jamshidian(1989)给出的。Jamshidian公式可以看作节中提到的对数正态期权定价公式的应用。(唯一所不同的是对数正态期权公式中的折现因子换成了零息票债券价格。)Jamshidian的贡献在于给出了M和V的具体公式,这两个公式在债券期权定价中与债券未来的价格有密切的联系。
一个欧式看涨期权,其执行价格为X,执行日期为T,标的物为s年到期本金为L的零息票债券。在0时刻,这个期权的价格可以用以下公式来表示:
其中:
注意当是时,波动率部分的简化为而简化为。
图展示了期权价值的计算过程。两个零息票债券价格和分别在E27和E28中计算,这里是通过用户定义函数VasicekCIRZero Value计算的(也可以通过在E6单元格改变相应的到期期限来计算)。而计算波动率,M和V需要一些中间的计算过程。经计算,执行期限为4年,标的物为10年期零息票债券的看涨期权价格为,列在H26单元格中。同样通过一个用户定义函数也可以计算出上述结果。
Valuing European Options on Zero-Coupon Bonds: 零息票债券的欧式期权定价
【书中235页的图】
图 Jamshidian的零息票债券期权定价方法
将短期利率波动性(B11单元格)从2%增加到4%时,期权的价值提升到。
Vasicek模型对附息债券欧式期权定价
Jamshidian也推导出一个更一般的模型为附息债券期权定价。重要的思路就是根据期权执行期之后的附息债券现金流,将附息债券期权分拆为多个零息票债券期权。对于一个执行期限为四年,标的物为10年期附息债券的期权来说,需要考虑第5年到第10年的利息支付以及最后到期的本金支付。在图中,相关的现金流列在E51到E56的单元格中,总量是(面值加上执行期之后的六次利息支付)。现在需要找到一个折现利率(比如)使得这些现金流的现值之和与期权的执行价格(,见E43)对应起来。计算的过程比较麻烦需要采用试错法,这里利用一个名为Jamshidianrstar的用户定义函数完成。
Valuing European Options on Coupon-Bearing Bonds:
附息债券的欧式期权定价
【书中236页的图】
图 用Jamshidian方法对附息债券期权定价
读者可以确认一下所计算出来的(%,见E46单元格)确实使得债券支付的现值总和(E47单元格)等于期权的执行价格(见E43)。而附息债券期权的价值只是单个零息票债券期权价值的简单加总,而单个的零息票债券期权的债券面值、执行价格和到期期限为别为,和,其价值使用VasicekZero Option函数计算。注意,计算这些单个期权价值时使用的是原来的短期利率(这里是6%),而不是。
CIR利率期限结构模型
Vasicek模型与CIR模型的主要不同之处就是短期利率波动性的表达形式不一样。在CIR模型中,波动率还取决于短期利率的开方,这样模型保证了短期利率不会出现负数。
在CIR模型中,短期利率的风险中性过程是随机过程,其形式如下:
这里的短期利率的波动率随着短期利率开方的增加而增加。这个模型防止了负利率的出现(只要参数值满足关系式)。
图展示了利用CIR模型对零息票债券的定价,见CIR工作表。可以看到此表的布局跟前面Vasicek工作表是相似的。
【书中237页的图】
图 CIR模型下的零息票债券价格,零息收益率和波动率
考克斯,英格索尔和罗斯证明了债券价格跟Vasicek模型一样存在一般形式。在s时刻支付1单位的零息票债券价格公式如下:
这里是r在t时刻的值。函数和的形式稍微有些不同,包含了一个新参数。根据CIR利率模型,10年期零息票债券的价值为,10年期零息收益率为%。同样的,可以通过改变在电子表格中s的值来获得利率期限结构,如工作簿中的图3。并且会得到与Vasicek模型一致的几种利率期限结构形状。
CIR模型对零息票债券欧式期权定价
为了计算CIR模型下零息票债券期权的价值,需要利用非中心卡方分布函数(取代布莱克-舒尔斯公式和Vasicek公式中的正态分布)。由于CIR公式非常复杂,因此在本书中不作具体描述,这里的主要任务是确保电子表格中的公式和相应的函数返回正确的数值。在CIR模型原文基础上的进一步研究很多,包括施罗德(1989)的论文,这篇论文给出了原文中分布函数的近似形式。图展示了零息票债券期权定价的过程,执行期限为4年,标的物为10年期零息票债券的看涨期权的价格为(H26单元格中)。通过用户定义函数可以得到相同的结果(H27单元格中)。
Valuing European Options on Zero-Coupon Bonds:
零息票债券的欧式期权定价
【书中238页图一】
图 利用CIR模型为零息票债券期权定价
CIR模型附息债券欧式期权定价
利用CIR模型和Jamshidian创立的方法(在Vasicek模型中出现过)对附息债券期权定价。同样的,根据期权执行期之后的附息债券现金流,可以将附息债券期权分拆为多个零息票债券期权。Jamshidian的贡献是找到了一个利率,使得单个期权执行价格的总和与附息债券期权执行价格一致。
图展示了定价过程。单元格E47中的利率是%,是根据Jamshidianrstar函数计算的。附息债券期权的价值是单个零息票债券期权价值的总和,最后得到的数值是(单元格H44中)。(在Vasicek模型中,期权的价值稍微低一点,为,见图。)
Valuing European Options on Coupon-Bearing Bonds:
附息债券的欧式期权定价
【书中238页图二】
图 利用Jamshidian方法对附息债券期权定价
Module 1中的用户定义函数
由于Vasicek模型和CIR模型有相同的元素,因此合理的做法是写一个函数同时允许Vasicek(imod=1)和CIR(imod=2)使用。和电子表格一样,函数也允许在a=0的情况下使用:
Function VasicekCIRZero Value(imod,a,b,r,nowyr,zeroyr,sigma)
‘ returns the Vasicek(imod=1) or CIR(imod=2) zero-coupon bond value
Dim syr,sig2,Asyr,Bsyr,rinf,gamma,c1,c2
syr=zeroyr-nowyr
sig2=sigma^2
If imod=1 Then
If a=0 Then
Bsyr=syr
Asyr=Exp((sig2*syr^3)/6)
Else
Bsyr=(1-Exp(-a*syr))/a
rinf=*sig/(a^2)
Asyr=Exp((Bsyr-syr)*rinf-((sig2*Bsyr^2)/(4*a)))
End If
Else If imod=2 Then
gamma=Aqr(a^2+2*sig2)
c1=*(a+gamma)
c2=c1*(Exp(gamma*syr)-1)+gamma
Bsyr=(Exp(gamma*syr)-1)/c2
Asyr=((gamma*Exp(c1*syr))/c2)^(2*a*b/sig2)
End If
VasicekCIRZero Value=Asyr*Exp(-Bsyr*r)
End Function
下面的函数用到以上函数计算出来的零息票债券价格,计算对数正态参数M和V。后边几行代码与OPTION2文件中的LNOption Value0函数相同,零息票价格替换折现因子。
Function VasicekZeroOption Value(iopt,L,X,a,b,rnowyr,optyr,zeroyr,sigma)
‘ returns the Vasicek zero-coupon bond option value
‘ uses VasicekCIRZero Value fn
Dim P0T,P0s,voT,BTs,sigmao,M,V,d2,d1,Nd1,Nd2
P0T=VasicekCIRZero Value (1,a, b, r, nowyr, optyr, sigma)
P0s=VasicekCIRZero Value (1,a, b, r, nowyr, zeroyr, sigma)
If a=0 Then
v0T=sigma*Sqr(optyr-nowyr)
BTs=zeroyr-optyr
Else
v0T=Sqr(sigma^2*(1-Exp(-2*a*(optyr-nowyr)))/(2*a))
BTs=(1-Exp(-a*(zeroyr-optyr)))/a
End If
sigmap=v0T*BTs
M=Log(L*P0s/P0T)*sigmap*2
V=sigmap^2
d2=(M-Log(X))/Sqr(V)
d1=d2+Sqr(V)
Nd1=(iopt*d1)
Nd2=(iopt*d2)
VasicekZeroOption Value=P0T*iopt*(Exp(M+*V)*Nd1-X*Nd2)
End Function
下面的函数试图通过试数法来找出折现利率,使得期权到期后的债券支付现值总和等于期权的执行价格。由于缺少斜率的计算公式,这里用另外一个公式代替斜率:
Function Jamshidianstar (imod,L,cL,X,a,b,rtest,optyr,zeroyr,coupyr,sigma,radj)
‘ replicates Goal Seek to find rstar in Vasicek or CIR coupon option value
‘ uses VasicekCIRBondnpv fn
DIM atol, rnow, fr1, fr, fdashr
atol=
rnow=rtest
Do
fr1=VasicekCIRBondnpv(imod,L,cL,a,b,rnow+radj,optyr,zeroyr,coupyr,sigma)-X
fr=VasicekCIRBondnpv(imod,L,cL,a,b,rnow,optyr,zeroyr,coupyr,sigma)-X
fdashr=(fr1-fr)/radj
rnow=rnow-(fr/fdashr)
Loop While Abs(fr)>atol
Jamshidianrstar=rnow
End Function
下面的公式给出了非中心卡方分布的近似形式,可参见施罗德(1989)的论文。(注意,在他的论文中,公式(10)中列出了c3,正确的情况应该是N(c3))。
Function CIRSankaranQ(z,nu,kappa)
‘ component for CIR Option Valuation(see Schroder)
Dim n,k,h,p,m,c1,c2,c3
n=nu
k=kappa
h=1-(2/3)*(n+k)*(n+3*k)/(n+2*k)^2
p=(n+2*k)/(n+k)^2
m=(h-1)*(1-3*h)
c1=1-h+*(2-h)*m*p
c2=1-h*p*c1-(z/(n+k))^h
c3=c2/(h*Sqr(2*p*(1+m*p)))
CIRSankaranQ=(c3)
End Function
小结
Vasicek和CIR利率模型是最早用于模拟利率并产生相应期限结构的模型。目的是为零息票债券定价提供一个解析解法,并改进忽略期限结构的布莱克-舒尔斯扩展方法。通过改变参数,这两个模型都能产生不同形状的期限结构。CIR模型将利率波动率与利率水平挂钩,这是Vasicek模型所不具备的性质。Jamshidian说明了如何利用零息票债券期权定价公式为附息债券期权定价。由于存在解析解,这些模型仍然非常重要,但是后来被拟合利率期限结构方法所取代,见下一章详细分析。
参考文献
Chan, ., G. A. and Karolyi, F. A. Longstaff and A. B. Sander, 1992, “An Empirical Comparison of Alternative Models of the Short-Term Interest Rate”, Journal of Finance, 47, 1209-1227.
Cox, J. C., J. E. Ingersoll and S. A. Ross, 1985, “A Theory of the Term Structure of Interest Rates”, Econometrica, 53, 385-407.
Jamshidian, F., 1989, “Computing the Constant Elasticity of Variance Option Pricing Formula”, Journal of Finance, 44, 211-219.
Vasicek, O. A., 1977, “An Equilibrium Characterization of the Term Structure”, Journal of Financial Economics, 5,177-188.
第16章 拟合利率期限结构
前一章的出发点是Vasicek与考克斯,英格索尔和罗斯(CIR)的短期利率连续随机模型。根据这些模型,可以得到零息票债券价格的解析解,在此基础上发展了几种债券期权定价的方法。本章作为一个对比,将使用离散的二叉树为短期利率建模。这么做的目的是根据零息票债券的价格,拟合(或者说对应)当前的期限结构,并根据这个模型为债券期权定价。
节中,我们曾举过一个利率二叉树的例子,例中,允许利率跟当前的零息票债券价格对应起来。本章将说明这种利率树是如何计算出来的,并使得利率对应当前的期限结构和相应的利率波动率。这时,不存在解析解,需要利用迭代方法产生利率二叉树。
在股票期权一章中,我们构造二叉树的出发点是两个已知量,股票现在的价格S和(无风险)利率r,根据这两个量,就可以得到期权执行日股票价格的分布。因此,二叉树被用于期权定价,或者任何由二叉树节点产生的现金流定价。
而在债券期权二叉树中,两个已知量为零息票债券价格P和债券到期时的现金流(等于1),根据这两个已知量就可以构造一个利率二叉树。二叉树从现在的短期利率出发,在以后的每一个时间点上,迭代过程保证了利率能跟市场上观察到的零息票债券价格对应起来。在二叉树的每个时间点上,处于节点的利率都与一个反映利率波动性的公式联系起来。通过保证利率树正确地为每个时间点的零息票债券定价,就可以解出剩下未知的利率。这样,利率二叉树与股票价格二叉树不同,有一个随时间变化的漂移项。整个模拟过程就是不断的延长时间点,直到债券到期。
相应的模型可以见工作簿文件文件。文件中有两个相关的简单例子(LnNormal和Normal),还有两个关于复杂的布莱克,德曼和托伊(BDT)树的应用(一个描述了整个树过程是如何建立的,另外一个利用这个模拟树为零息票债券期权定价)。简单例子中采用了连续复利利率,而BDT树采用离散复利利率。
对数正态分布利率树
图展示了不同期限的零息票债券价格(还有由此得到的当前的利率期限结构)和波动性。计算出来的利率树与零息票债券价格和利率波动性一致,列在B48:E51单元格中。实际上,为了示范这种一致性,H和J列中的债券价格和波动性是根据利率树反推出来的。下面说明利率树是如何被构建的。
【书中244页图一】
图 最终的利率树,与零息票债券价格和波动性一致
图展示了与图相同的市场信息,包括零息票债券价格和短期利率的波动性。同时表中还有一个两期的利率树和一个两期的价格树,标的物是两年期的债券,面值为1。每一个时期都为一年。
【书中244页图二】
图 零息票债券价格和收益率,以及短期利率的波动率
在对数正态树中,每一个时间点i上,临近的两个利率(往上和往下)之间有以下的关系:
这个关系也可以写成,单元格C13对这个公式进行了说明。然后,就可以利用两期的短期利率树对两期的零息票债券的现金流进行定价。债券的定价在零息票债券树中进行,这里假设利率往上和往下的概率是一样的,而折现因子就是短期利率树中的利率。
例如,假设现在的一年期利率为%(B12单元格中),短期利率在下一期以相同的概率上升到或下降到。我们尝试给出的值为7%(C12单元格中),的计算公式在C13中,公式中采用了C12中的利率和H7中的利率波动率,得到的结果是%。通过这些利率对两期利率树进行折现,得到两年期的零息票债券价格为。注意C16中的公式,其计算的结果是到期前一年债券的期望价格(当利率上升时,用7%作为折现率)。
现在问题涉及到一个未知的元素(C12,现在是7%)和一个已知的债券价格(这里两年期零息票债券的价格为,在D7单元格中)。为了达到一致,单元格C12中的数值需要进行修改使得B16单元格的公式值等于,或者,一种更好的方法是使得B18的检验值(两年期零息票债券价格与B16的差再乘上100)等于0。为了达到这个目的,在债券价格树中使用单变量求解函数,将B18中的值设为0,然后改变利率树中C12的值。如图所示,C12的值从7%改变到%。价格树将往上和往下变动的概率看成是相等的,而每个节点上的公式与前面介绍的股票期权定价的二叉树十分相似。
利率树是建立在未来的时间点上的,从第2个时间点开始(因为开始的零息票收益率可以应用在第1个时间点上)直到期权标的债券到期的时刻,在每一个时间点上,都采用单变量求解函数。单变量求解函数保证了节点的漂移与市场价格暗含的漂移对应起来。
【书中245页图一】
图 用Goal Seek函数寻找短期利率树,使其产生正确的零息票债券价格
而一个四期的利率树,见图,可以通过同样的方法获得。使用BinZeroRate Tree函数会使过程变得更加简单,见图。根据利率树得到的零息票债券价格(H48:H51)和短期利率波动率(在J48:J50单元格中)与前面使用的价格和波动率一致。
【书中245页图二】
图 用单变量求解函数完成短期利率树,并保证与零息票债券价格一致
通过两个向量的表示,可以更加简便地描述短期利率树。X向量代表每个时刻短期利率树节点上的最高利率。K向量代表与此对应的往下变动的利率,通过来计算。
如果计算一种称为阿罗-德布勒的价格树来代替对应债券价格而建立利率树模型,其效率将会更高。但是,重复使用单变量求解函数是一种更容易地理解建立树过程的方法。
到此,得到了一个短期利率二叉树(对应现在的零息票债券期限结构和利率波动率),可以对债券期权进行定价。
正态利率二叉树
刚介绍的LnNormal工作表与下面将要介绍的Normal工作表的唯一区别在于二叉数短期利率的波动率计算上。在正态树中的每一个时刻i,节点连接的两个短期利率有以下的关系:
这个式子可以改写成,单元格C13中应用到了这个公式。注意,对应短期利率的波动率(H7:H9单元格)。在此之后,建立利率树的过程与前面的工作表完全相同(参见图)。
【书中246页图一】
图 在利率服从正态分布的假设基础上建立短期利率树
建立利率树的结果如图。这里采用了BinZeroRate Tree用户定义函数。(函数中的第一个参数用来决定利率到底是服从对数正态还是正态分布的。)
【246页图二】
图 正态分布假设下的利率树
BDT树
下面介绍的布莱克,德曼和托伊(BDT)树是在简化的对数正态树基础上发展而来的。但有一点不同,利率树对应的是零息票收益率的波动性而不是对数短期利率的波动性。
在LnNormal工作表中有以下关系式:
这个公式通过短期利率的波动性连结了二叉树节点上的两个短期利率。在BDT工作表中,建立了BDT模式的对数正态树,并有以下关系式:
这个关系式将BDT树中的收益率和零息票收益率的波动性连接起来了。
这里有两点要注意的:第一,在前面的例子中,先给定短期利率的波动率然后将利率与利率漂移项对应起来,现在这个过程被一个最优化过程取代了,最优化过程尝试同时对应波动率和漂移项;第二,这个最优化过程所给出的结果不一定与零息票债券价格严格对应。
在图中,我们说明了如何利用Jamshidian(1991)方法去对应BDT树中的零息收益率和波动率。BDT工作表中有两个不同的部分:E列到H列对应零息票收益率,而K到N列既对应零息收益率也对应收益率波动率。在这个工作表中,利率是在离散复利形式下计算的。
【书中247页图】
图 BDT方法下的短期利率
克卢洛和Stricklan(1998)通过阿罗-德布勒状态价格对树的构建过程给出了一个更详细的结果。首先最关心的是明确BDT树和简单的对数正态树之间的区别,其次才确保所构建的树与零息票债券价格和收益率波动率严格对应。
利用图的布局,建立了一个四期的BDT树,对应零息票债券的价格(H7单元格)。对比之下,初始状态的二叉树(通过猜测的初始值区域F9:H11)给4年期零息票债券的定价为(E38单元格)而通过Jamshidian方法给出的价格为(L38单元格)。
和向量是对应零息票债券价格和收益率波动率的充分必要条件。和向量代表了零息票债券在1时刻(债券还有3年到期)向上和向下状态下的价格。这里和的精确值是和(O9和O10单元格),相比于Jamshidian树中的和(M38和M39单元格)。
将注意力集中到第一部分的F到H列,注意到第7行零息票债券价格的精确值(根据第5行的零息收益率推导)与第12行的估计价格(根据第9行的值推导)一致。但是第20行估计的收益率波动性与15行的零息收益率的精确值并不对应。可以使用单变量求解函数三次(F,G和H列),通过改变第9行的值,将22行的检验值设定为0。这样就能保证与收益率波动性保持一致。短期利率和零息票债券价格的二叉树结果在32到42行,L到P列的表格中列出。
通过Jamshidian方法和时间间隔为1年的所构造的零息票债券价格树与4年期的零息票债券的价格(,H7单元格)并不完全一致,通过初始的值,其得到的债券当前价格为(E38)而解法给出的结果是(L38)。Bjerksund和Stensland(1996)分析了通过进一步迭代能够达到的精确度。
用BDT树为债券期权定价
建立短期利率二叉树的目的是为了建立零息票债券价格二叉树,从而为零息票债券期权定价。ZCBOption工作表中使用了BDT树,为一个四年期的欧式和美式看跌期权定价,其标的物为一个10年期的零息票债券。
如图所示,使用一系列零息票债券收益率(第8行的zvec)和收益率波动率(第9行sigvec)直到第10年。通过用户定义数组函数BDTKvec(第15行)和BDTXvec(第16行)获得短期利率树。从第0到第3步的数值与前面BDT工作表的短期利率树的结果一致。然后,用短期利率树建立一个期权标的债券的价格树(图)。二叉树给出债券的价值为(B36单元格)对应于(L13)的市场价格。
之后,期权的定价过程与股票期权相似。在执行日当天,期权的收益状况通过零息票债券价格和执行价格计算得出,然后顺着二叉树倒推到现在就可以得到期权的价值。二叉树中假设往上和往下变化的概率都为,使用利率二叉树中的相应利率作为折现率。
【书中249页图一】
图-ZCBOption工作表中债券期权定价的短期利率树
【书中249页图二】
图 -ZCBOption工作表中通过零息票债券价格树为债券期权定价
执行价格为的欧式看跌期权的价值为(B51单元格中),美式期权的价格稍微高一些,为(B62单元格)。
BDT树建立在简单对数正态树基础之上,它受欢迎的原因是通过市场价格可以容易地得到零息票债券价格的期限结构和收益率波动率。根据Bjerksund和Stensland的建议,只需增加少数的几步迭代,就可以更准确地对应当前的期限结构。
Module 1中的用户定义函数
带参数imod的函数允许在即期利率服从正态和对数正态两种情况下使用。牛顿-拉夫森(NR)搜寻法采用了一个近似法去求解:
Function BinZeroRate Tree (inorm,zpvec,volvec,L,delt)
' generates binomial spot rate tree
' inorm=1 for normal rates,2 for in-normality
' continuous interest rates only
' uses BinZeroPrice fn
Dim radj,atol,zpk,vkd,rk,fr1,fr,fdashr
Dim i As Integer, j As Integer, k As Integer,n As Integer
Dim RTmat() As Variant
n=(zpvec)
ReDim RTmat(n,n)
radj=
atol=
RTmat(1,1)=-(Log(zpvec(1)/L))/delt
' solve for RTmat column-by-column
For k=1 To n
zpk=zpvec(k)
vkd=2*volvec(k-1)*Sqr(delt)
' use NR search to solve for rk,with finite difference fr1
rk=RTmat(1,k-1)
Do
RTmat(1,k)=rk+radj
For i=2 To k
If inorm=1 Then RTmat(i,k)=RTmat(i-1,k)-vkd
If inorm=2 Then RTmat(i,k)=RTmat(i-1,k)*Exp(-vkd)
Next i
fr1=BinZeroPrice(L,RTmat,k,delt)-zpk
RTmat(1,k)=rk
For i=2 To k
If inorm=1 Then RTmat(i,k)=RTmat(i-1,k)-vkd
If inorm=2 Then RTmat(i,k)=RTmat(i-1,k)*Exp(-vkd)
Next i
fr=BinZeroPrice(L,RTmat,k,delt)-zpk
' finite difference approximation to derivative
fdashr=(fr1-fr)/radj
rk=rk-(fr/fdashr)
Loop While Abs(fr)>atol
Next k
BinZeroRate=RTmat
End Function
这里NR搜寻法用一个解析公式去求解
Function BDTPUvec(z0,zvec,sigvec,delt)
' replicates Goal Seek to find initial PU vector in BDT
' discrete interest rates only
' see Jamshidian(1991)
Dim atol,r0,jdt,zpj,puj,sigi,pdj,fval,fdashval
Dim j As Integer, n As Integer
n=(zvec)
ReDim puvec(n)
atol=
r0=(1+z0)^(delt)-1
For j=1 To n
jdt=j*delt
zpj=(1+zvec(j))^-((j+1)*delt)
puj=zpj*(1+r0)
sigi=sigvec(j)
Do
pdj=2*zpj*(1+r0)-puj
yuj=(puj^(-1/jdt))-1
ydj=(pdj^(-1/jdt))-1
fval=*Log(yuj/ydj)-sigi*Sqr(delt)
fdashval=-(
puj=puj-(fval/fdashval)
Loop While Abs(fval)>atol
puvec(j)=puj
Next j
BDTPUvec=puvec
End Function
PU向量被用于产生K和X向量,从而给出短期利率树:
Function BDTKvec(pucvec,pdvec)
' from Pu and Pd vectors to approximation vector k in BDT
' see Jamshidian(1991)
Dim i As Integer, n As Integer
Dim kvec() As Integer
n=(puvec)
ReDim kvec(n)
kvec(1)=((1/puvec(1))-1)/((1/pdvec(1))-1)
For i=2 To n
kvec(i)=((puvec(i-1)/puvec(i)-1)/((pdvec(i-1)/pdvec(i)-1)
Next i
BDTKvec=kvec
End function
Function BDTXvec(puvec,pdvec,kvec)
' from Pu and Pd vectors to approximation vector X in BDT
' see Jamshidian(1991)
Dim Prvec0
Dim i As Integer, n As Integer
Dim xvec() As Variant
n=(puvec)
ReDim Xvec(n)
Xvec(1)=((1/*(puvec(1)+pdvec(1))))-1)/(*(1+1/kvec(1)))
For i=2 To n
xvec(i)=(((puvec(i-1)+pdvec(i-1)))/(puvec(i)+pdvec(i)))-1)/((*(1+1/kvec(i)))^i)
Next i
BDTXvec=xvec
End Function
小结
建立二叉树的过程解释了为什么债券期权定价比股票期权定价复杂得多。但是,对应利率期限结构已经成为金融市场债券期权定价实务中的先决条件。幸运的是,在股票期权定价中我们已经广泛的使用风险中性定价和二叉树。本章的重点是推导利率二叉树的迭代方法上。用于说明如何为零息票债券期权定价的BDT树方法是深受从业者欢迎的。
参考文献
Bjerksund, P. and , 1996, “Implementation of the Black-Derman-Toy Interest Rate Model”, Journal of Fixed Income, 6, 67-75.
Black, F., and , 1990, “A One-Factor Model of Interest Rates and its Application To Treasury Bond Option”, Financial Analysts Journal, 46, 33-39.
Clewlow, L. and , 1998, Implementing Derivatives Models, John Wiley & Sons, Chichester.
Jamshidian, F., 1991, “Forward Induction Construction of Yield Curve Diffusion Models”, Journal of Fixed Income, 1, 62-74.
附录 其它VBA函数
本附录提供了另外一些用户定义函数,这些函数用于短期预测、ARIMA建模、样条、特征值和特征向量计算。每个应用部分都有简要的介绍。在本附录中,相应的VBA函数比电子表格本身更重要,这些程序可以在VBE代码窗口运行。本章相应的工作簿叫。
预测
在4Cast工作表中的函数都是基于时间序列分析的,特别是用于分析产品以往的月销售数据。时间序列预测方法假设现在的销售模型在将来继续有效,因此这些模型在短期预测中比长期预测更有效。销售数据通过不同的方法进行“平均”,即保证最近的数据比很早以前的数据赋予更大的权重。短期预测就是将最近的平均值作为下一期的数据。
首先,可以通过目测和其它的技巧来检验数据服从什么模型。如果数据是围绕一个平均“水平”进行波动,那么这个模型假设有以下形式:
data=level+error (A类)
如果数据表现出一种趋势(平均水平随时间变动),那么模型有以下形式:
data=level+trend+error (B类)
如果数据围绕一个跟季节有关的固定因素变动,那么模型假设有以下形式:
data=(level+trend)×seasonal_factor+error (C类)
这里季节因素的均值为1。
在决定了数据适用的模型以后,就可以选择方法和参数来进行逐期的预测。根据历史数据,可以预测下一期数据的平均值。对于A类数据来说,可以通过移动平均的方法进行预测,而另外一种方法就是用指数加权平均。指数加权平滑方法在这三类模型中都可以采用。用户定义函数Esvec,ESHoltvec和ESWintervec返回各类数据类型的指数平滑预测值。每个函数都需要输入数据(指dvec)和不同的参数值。
指数平滑预测所采用的公式如下:
新预测值=*data+(1-)*(旧预测)
平滑常数(在0与1之间)决定了预测值随新数据改变的速度。
4Cast工作表包含了三个不同类型的销售数据(A类在B列中,B类在I列中,C类在O列中)。
首先观测A类销售数据, D列给出了销售的六期移动平均预测值,在F列给出平滑常数=的指数移动平均预测值。函数MAvec根据输入值(B11:B34)给出计算结果(D11:D34),这个过程中使用了转置函数对输出结构进行了转置。F列中的输出结果是根据Esvec函数计算的,输入数据在B11:B34,F5,F3单元格中,同样使用了转置函数。
用户定义函数(如Mavec,ESvec等等)的代码从变量的初始值开始计算,然后通过不断更新数列的平均值进行预测。代码中的主要变量都含有vec——fvec代表预测,lvec代表每期的平均值(level),tvec代表每期的趋势值(trend)。每个向量都是n维的。对于季节性数据,需要将第i期转换为相应的季节,并用用户定义函数中的MMod指明。
所有这些模型都假设未来销售水平可以根据过去的数据进行预测。例如,移动平均是一个有限数据点的等权重平均方法。指数平滑中数据的权重是一个呈几何级数变化的数列。在上述两种方法中,都是通过对历史数据的组合来预测未来,而权重则依赖于相应的方法。与之成对比的是,博克斯-詹金斯的方法(下一节介绍)通过决定最优的权重来进行预测。
ARIMA模型
一些简单的ARIMA模型(Autorogressive integrated Moving Average)可以在Excel中应用,同时使用规划求解最优化过程。通常这些模型的估计值都是在一个黑箱子中计算出来的——在软件中输入数据,等待,然后得到结果。很少知道软件的计算机理是什么。为了发展对Excel的应用,有必要更进一步地分析这些计算,通过第一阶段产生的参数估计量,可以确保第二阶段(使用规划求解)进行得更顺利。第一阶段的估计量可以通过VBA函数计算,而第二阶段需要使用规划求解,这都能通过宏自动执行。
下面介绍的ARIMA建模过程是根据博克斯和詹金斯的方法进行的(Pecer,1994)。第一步要检验数据是否平稳。如果数据不是平稳的,就必须进行一定的变换(通常是差分)获得平稳的数据。对变换后的平稳数据选择相应的ARIMA模型。第二步估计ARMA模型中的参数,通常使用非线性最小二乘法(NLS)。ARMA模型的阶数通常是根据数据的自相关和偏自相关函数决定的。第三步通过适当的检验方法(通常是Q检验)检验ARMA模型的残差项是否服从白噪声过程。第二和第三步可以重复进行直到所有的p和q组合都尝试过。尽管有其它一些方法帮助我们选择最好的模型,但是这个过程仍然需要艺术和经验。在工作簿中提供了部分的诊断工具,包括计算样本自相关函数和偏自相关函数的计算函数,以及Q检验函数。
Excel中应用ARIMA模型的第一步是通过画图和差分来检验数据是否是平稳序列。在第二步中,通过用户定义函数ACFk和PACFk(返回滞后阶数为k的自相关和偏相关系数)画出自相关函数和偏自相关函数图。通过观察这些图,可以直接决定ARMA模型中AR和MA项的参数。在ARMA工作表中,有三种不同序列(D,G和N,34行及以下)的模型估计过程。通过检验这三个序列的AC和PAC函数(第7到第18行),可以看出序列1适用AR模型,序列2适用MA模型,序列3适用ARMA模型。通过使用函数和宏,逐一说明一阶自回归模型(AR(1))、二阶移动平均模型(MA(2))和ARMA(1,1)模型。另外有一个返回ARMA模型参数估计量的用户定义函数。
只有自回归项(AR)的ARMA模型的估计需要用普通最小二乘回归,参数的精确估计量可以在一步计算中获得。函数Arpvec中使用LINEST函数,并返回数据序列与其滞后值的多元线性回归的结果。在ARMA工作表中,Arpvec函数用于估计AR(1)过程的参数。
包含移动平均项(MA)ARMA模型的参数估计过程有两步,用初始估计量作为最优化过程的输入量,并产生最后的估计量。博克斯和詹金斯(1984)说明了如何找出MA参数的初始估计量,他们的想法可以由Maqvec0函数实现。计算数据的(q+1)自协方差开始,然后通过一个迭代过程(将模型的参数估计量设为0)使得残差方差最小进行估计。在电子表格中,这些初始估计量(在G26:G28单元格中)用于给出MA(2)过程的一系列残差(误差)。注意,一定时期的残差项不依赖于数据,而依赖于之前两期的残差(见I34单元格的公式)。在选择初始估计量之后,得到残差的平方和(G30单元格)的值为。现在使用规划求解去寻找新的参数组合,使得残差平方和最小。可以通过Excel命令或者VBA宏来实现。子程序(MA2qvec)的代码可以参见BM模块。迭代以后得到的参数估计量在I26:I28单元格中,而模型的残差平方和也减少到。
对于ARMA模型,需要AR参数的初始估计量,这些估计量可以通过尤尔-沃克方程式获得。AR的参数初始估计量已经计算出来(使用ARMApqvec0函数)(见N26:N28单元格)。在电子表格中,这些参数初始估计量被用于给出ARMA(1,1)过程的一系列残差。注意,对于ARMA(1,1)模型来说,残差既依赖于数据序列也依赖于前一期的残差(如P35单元格中的公式所示)。通过参数初始估计量算得的残差平方和为(N30)。如同MA(2)模型一样,规划求解最优化过程被用于计算最后的参数估计量(P26:P28单元格)。规划求解最优化可以通过子程序ARMA11pqvec来控制,并需要使用Ctrl+Shift+C键盘按钮组合。
样条
样条法是一个根据x-y数据点产生平滑曲线的有效方法。采用样条法的目的可能是为了插值(曲线需要通过所有数据点)或者是一个更一般的拟合过程(曲线要达到最优拟合效果,而不一定通过所有的数据点)。对比于拟合高阶多项式的方法,样条法的一个优点是只需要少量的参数。低阶多项式用于分段拟合连续数据点。通过选择多项式的参数,可以产生平滑的曲线与数据点对应起来。这里将集中分析三次多项式,这样可以保证函数的一阶和二阶导数连续。模块(Module C1)中使用的函数可以计算标准的三次多项式样条。另外,还列举了一个能产生一系列多项式样条的函数,通过这个函数可以找出最优的多项式样条。
先从数据点开始。在样条的术语中,x值表示节点,y值是节点相应的函数值。拟合样条就是用三次多项式连接相邻的节点,并且使得曲线光滑。对于三次样条来说,可以通过保证节点两边的二阶导数连续而获得光滑的曲线。CubicSplinezvec函数返回每个节点的二阶导数(称zvec),用节点向量(kvec)和函数值(ykvec)作为输入变量。函数计算两个数组,一个对角矩阵(Tmat)和一个向量(vvec),然后通过矩阵相乘产生一个二阶导数向量(zvec)。通过CubicSplinezvec函数返回的导数值用于第二个函数CubicSplineValue。这个函数用于对其它x值(非节点)进行插值,产生一组新的y值。对于一个自然的三次样条函数来说,样条的二阶导数在每一段x值的末端一定为0。对于特殊的x值来说,第一步就是要确定它落在哪两个节点之间,然后选择适当的样条来计算其对应的函数值。样条值的计算用到了一阶(br)和二阶(nzvec)导数。
CubicSplineValuey函数返回x值对应的样条值。作为一个巩固练习,读者可以思考如何写一个函数,计算在一系列节点范围内的x值的对应样条值。
标准的样条非常有用,但是一个潜在的问题就是当改变某一个样本点时,会对整个样条的估计结果产生影响。例如,改变单元格C20(Spline 工作表)中的数值,从改为,注意到二阶导数向量发生了改变。从图一中,还可以发现修改以后的样条函数仍然保持平滑性。
相比之下,基本样条法(B样条)的一个好处就是当一个数据点发生变化时,样条函数能够在局部适应这种变化,而不会发生全局的变动。这个方法是通过定义一组“基本样条”来实现的。所谓基本样条就是函数值在两个相邻节点区间内取正值,而在其它范围取0。每一段基本样条对应一个不同的节点。通过建立一系列的基本样条,造一个函数,将这些不同节点的样条值联系起来。用户可以选择要估计的节点位置(包括落在样本点之外的范围),并通过回归方法计算出最佳拟合的参数,从而计算出相应的样条值。可以看到,尽管三次多项式样条对数据的拟合程度不错,但是基本样条法(B样条法)是一个更为一般的拟合方法(对所有数据点的最佳拟合)。基本样条是拟合利率期限结构的一个常用方法。
在标准样条方面,这里只是说明了三次多项式样条形式,而每一段样条在相应的区间上都取正值。对于一个给定的x值,我们需要估计出每一段样条的参数,然后求出相应的样条值。而函数CubicBSplineValuey,将矩阵(包含节点值)作为输入变量,通过循环关系产生基本样条值。所谓循环关系是指在第一步计算三个基本样条,在第二步计算两个,在第三步计算单个的样条。
特征值和特征向量
尽管特征值在物理和工程学中扮演着重要角色,但是他们在金融领域的主成分估计中也是相当重要的。一个典型的应用就是将风险(通过资产收益的方差和协方差矩阵来表示)分解为一系列的线性独立变量(主成分或者特征向量)。特征值反映了不同特征向量所引起的风险比例。
尽管对于大型的矩阵有更好的方法,但雅各比方法在计算实对称矩阵方面简单有效。这种方法可以将输入矩阵(实对称)经过一轮变换以后转换为对角阵。每一次变化都保持对角元素(最后会变为特征值)的和不变,同时减少非对角元素的平方和。这个过程直到平方和低于某个数值时停止(这个值为10-19,称为容忍水平)。
函数Eigenvaluesevec(在Module D1中)是主要的程序,而中间的函数都是进行雅各比转换。首先,计算转换角(通过Jacobivec函数),然后建立转换矩阵(在JacobiPmat函数中),最后得到更新后的输入矩阵(JacobiAmat函数)。主程序的每一次循环产生一个新的输入矩阵,并计算非对角元素的平方和。当达到容忍水平时,输入矩阵的对角元素被作为特征值。
相应的特征向量的计算(在EigenvectorsEmat函数中)有相似的过程,但还需要另外一个矩阵(开始时为一个单位阵)进行更新。当达到容忍水平时,特征向量就是这个矩阵的列向量。
参考文献
Box, . and , 1984, Time Series Analysis: Forecasting and Control, Molden Day, San Francisco, CA
Pecar, B., 1994, Business Forecasting for Management, McGraw-Hill, Maidenhead