Anyone attempting to generate random numbers by deterministic means is, of course,living in a state of sin.
任何人想要以确定的方式产生随机随机数,则不异于活于罪恶之邦。
John von Neumann
约翰˙冯˙纽曼
第一节 蒙地卡罗模拟法概要
对于一个不确定的随机现象,我们通常会以机率的方式加以描述。例如,一年后(期末)的大盘指数(PT),相对于今日(期初)指数(P0= 8,000)可能有60%的上涨。然而,如果想要有更进一步的说明,则我们可能要对随机现象中的变量,有更明确的假设。
我们可以假设随机变量X表一年后的对数报酬率,具有常态分配的性质,且其年平均数为4%,年标准偏差为30%。则X表示如下,
…………………………………………………(2.1.1)
由于常态分配的性质已广为人知,所以X的未来分布情况也可自然可以得知。例如,X的平均数为0.04。因此,一年后指数的平均数为,
再如,X的1%的单边最大损失为,
此时对应的一年后指数为,
换句话说,有1%的机率一年后的指数会跌到4139以下。
然而,如果我们创造了一张一年期的金融契约,它的期末偿付为,
…………………………………………………….(2.1.2)
我们想要知道此金融契约一年后1%的最大损失,或是期末偿付的平均数,那就不是一件简单的事情了。一方面,期末偿付函数为一具有max()函数形式的随机变量;另一方面,max()函数之中包含了期末指数的一次式与二次式。
但是,如果我们已经知道期末指数的分配形式(2.1),我们可以反复的由随机数中,抽取随机变量X值,X0,X1,X2,…,XN。再由X得到PT,代入(2.1.2)式的期末偿付,便可得到期末偿付的出象。据以描绘出期末偿付的分布型式,并求的相关的统计量,如平均数、标准偏差、1%最大损失等。
上述方法便是蒙地卡罗模拟法的基本精神,然而此法的成功与否,仰赖于下面条件。首先,随机变量X的分配假设是正确无误的。其次,随机数的抽取确实反映了随机随机数的要求。前者的正确性需要由财务理论与数据实证来回答。后者则须要数值方法与统计理论来回答。
在过去40年中,Black-Scholes的选择权定价理论,主导了前20年的金融市场,1993的Heston模型可说是另一个新的里程碑。本书将以Heston模型为主轴来说明,但是在进入第二篇的内容之前,我们还是以Black-Scholes模型做为程序说明与撰写对象。毕竟这是大部分金融从业人员都已熟悉的内容,也是现代金融的基石。
第二节 C#开发环境介绍与使用内建随机数
由于随机数的质量在仿真时至关紧要,因此我们须对计算机如何产生随机数,详加探究。我们先使用程序内建随机数,然后再使用市场上已有口碑的高质量随机数生成器。程序的撰写是必然的事,本节简单说明使用的环境,并测试我们的讨论内容。读者如果对开发环境不熟悉,建议找一本介绍VisualStudio与C#入门使用的书籍。
大部分的程序语言在内建的函数中,都有随机数函数。在C#亦有提供此一随机数对象,Random,此对象位于System的命名空间之下。使用时需先建立对象,对象初始化时可以给起始种子,也可以不给起始种子。如果不给起始种子,程序会以系统时间作为起始种子。
下面以Visual Studio2017作为开发环境,说明如何使用系统内建Random对象。首先,打开Visual Studio2017,从功能选单中《档案╱新增╱项目》的方法,产生CPUST方案以及CSRandom项目,选取控制台应用程序,按确定。
Visual Studio自动为我们完成项目框架的设定如上图,在中央的程序代码编辑区,输入下面的程序代码。
#001 static void Main(string[] args)
#002 {
#003 const int OBS =100000;
#004 Random Rnd = new Random(1234);
#005 double[] X = new double[OBS];
#006
#007 for (int i = 0; i< OBS; i++)
#008 {
#009 X[i] = Rnd.NextDouble();
#010 }
#011
#012 double Sum = 0.0;
#013 for (int i = 0; i< OBS; i++)
#014 {
#015 Sum = Sum + X[i];
#016 }
#017 double Mean = Sum / OBS;
#018
#019 Sum = 0.0;
#020 for (int i = 0; i< OBS; i++)
#021 {
#022 Sum = Sum + (X[i] - Mean) * (X[i] - Mean);
#023 }
#024 double Var = Sum / (OBS - 1);
#025
#026 Console.WriteLine("Meanof Uniform(0, 1) = " + Mean.ToString());
#027 Console.WriteLine("Varianceof Uniform(0, 1) = " + Var.ToString());
#028 Console.ReadKey();
#029 }
程序行表2.1
以鼠标点选左侧方案总管中的CSRandom项目,从功能选单中《建置╱建置CSRandom》的方法,编译项目。我们可以看到画面左下方的讯息列出现建置成功的讯息。按F5快捷键,执行程序画面如下。
程序代码#003首先定义100,000个观察值,#004中产生Random对象,我们以1234作为起始随机数传入来初始化对象。#005宣告X数组储存产生的随机数值。#007~#010为呼叫产生随机数的回圈。我们在#009叫用NextDouble()方法,产生Uniform(0,1)的均等随机数,十万个。#012~#017计算X数组的平均数,#019~#024计算X数组的变异数。#026输出平均数,#027输出变异数。#028的用途是我们由VisualStudio中执行此程序后,不会输出后立刻关闭,它会等我们按下任一按键后,才会关闭。
由维基百科中可得知,Uniform(a, b)的均等随机数,平均数与变异数分别为,
相较于模拟的输出结果,误差分别为0.000871与0.000024。误差百分比为0.1742%与0.0288%。