C---高级量化金融-全-

C++ 高级量化金融(全)

原文:zh.annas-archive.org/md5/0F4C0352DDAD129C371BB77E739BD2E4

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

定量金融是一个高度复杂的跨学科领域,涵盖了数学、金融和信息技术。成功地驾驭它需要来自许多来源的专业知识,如金融衍生品、随机微积分和蒙特卡洛模拟。至关重要的是,它还需要将理论有效地转化为实践的能力。

在《使用 C++进行高级定量金融》中,我们将带领读者进入这个激动人心的领域。解释了用于定价金融衍生品的关键数学模型,以及用于解决这些模型的主要数值模型。特别讨论了股票、货币、利率和信用衍生品。该书还介绍了如何逐步在 C++中实现这些模型。书中提供了几个完整的、可立即由读者测试的示例,以支持和补充他们的学习。

本书内容包括

第一章,《什么是定量金融?》,简要介绍了定量金融,将主题限定为使用 C++进行期权定价,并描述了本书的结构。

第二章,《数学模型》,概述了现代金融市场中用于定价衍生品的基本模型。

第三章,《数值方法》,回顾了用于解决第二章《数学模型》中描述的数学模型的三个主要数值方法的主要家族。

第四章,《C++中的股票衍生品》,演示了使用 C++对基本合同(欧式看涨/看跌)和高级合同(多资产期权)进行股票衍生品的具体定价。

第五章,《使用 C++进行外汇衍生品》,演示了使用 C++对基本合同(连续屏障)和高级合同(期末屏障)进行外汇衍生品定价。

第六章,《使用 C++进行利率衍生品》,展示了使用 C++对基本合同和高级利率互换(IRS)进行利率衍生品定价。

第七章,《使用 C++进行信用衍生品》,演示了使用 C++对基本合同(默顿模型)和高级合同(信用违约互换(CDS))进行信用衍生品的具体定价。

附录 A,《用于期权定价的 C++数值库》,简要介绍了可用于期权定价的各种数值库。

附录 B,《参考文献》,列出了本书各章中使用的所有参考文献。

本书所需内容

为了实现本书中描述的定价算法,您需要一些 C++ 和您选择的集成开发环境(IDE)的基本知识。我使用了 Code:Blocks,这是一个免费的 C、C++ 和 Fortran IDE,非常灵活和完全可配置。您可以从 www.codeblocks.org/ 下载它。您还需要一个 C++ 编译器。我使用了 MinGW,它是 GNU 编译器集合(GCC)的一部分,包括 C、C++、ADA 和 Fortran 编译器。这个编译器可以从 www.mingw.org/ 下载。

这本书适合谁

这本书非常适合量化分析师、风险管理人员、精算师和其他在量化金融领域工作的专业人士,他们希望快速查阅或亲自了解金融衍生品定价。在公司金融和/或风险管理衍生品课程中学习的研究生、硕士和工商管理硕士学生也会从本书中受益。对于对了解这些迷人的金融工具感兴趣的高年级本科生来说,这本书也可以有效地使用。需要基本的编程概念、C++ 编程语言和本科水平的微积分知识。

约定

random.cpp)."

代码块设置如下:

    for (int i=0; i < N; i++)
    {
      double epsilon = SampleBoxMuller();  // get Gaussian draw
      S[i+1] = S[i]*(1+r*dt+sigma*sqrt(dt)*epsilon);
    }

新术语重要单词以粗体显示。您在屏幕上看到的单词,例如菜单或对话框中的单词,会以这种方式出现在文本中:“在本书中,所有程序都是使用最新的标准 C++11 使用Code::Blocks (www.codeblocks.org) 和MinGW (www.mingw.org) 实现的。”

注意

警告或重要提示会以这样的方式显示在一个框中。

技巧

技巧和窍门显示如下。

第一章:量化金融是什么?

量化金融研究量化技术在解决金融问题中的应用。它涵盖了诸如管理投资基金和保险公司、制造公司和银行业的金融风险控制以及金融市场行为等多个领域。量化金融是高度跨学科的,建立在金融、数学和信息学的关键专业知识之上。

本书将重点关注量化金融的一个方面——使用编程语言 C++定价金融衍生品。在接下来的章节中,我们将描述构成量化金融的三个关键学科的主要特点:

  • 金融

  • 数学

  • 信息学

学科 1 - 金融(金融衍生品)

一般来说,金融衍生品是两个当事人之间同意在未来交换一个或多个现金流的合同。这些现金流的价值取决于未来事件,例如某些股票指数或利率的价值是否高于或低于某个预定水平。因此,这种未来事件的激活或触发取决于一个称为标的物的可变数量的行为。金融衍生品得名于它们的价值来源于另一种金融工具的行为。

因此,金融衍生品本身没有内在价值(与债券或股票相反);它们的价格完全取决于标的物。

衍生合同的一个关键特征是,它们未来的现金流是概率性的,而不是确定性的。衍生合同的未来现金流取决于未来事件。这就是为什么衍生品也被称为有条件权利。这一特征使得这些类型的合同难以定价。

以下是最常见的金融衍生品类型:

  • 期货

  • 远期

  • 期权

  • 掉期

期货和远期是两个当事人之间的金融合同。一方同意在某个预定日期(到期日)以某个预定价格(交割价格)从另一方购买标的物。一个例子可能是一份一盎司银的一个月远期合同。标的物是一盎司银的价格。在合同成立时(今天,t=0)不会发生现金流的交换,但只会在到期时(t=T)发生。这里的t代表可变的时间。远期合同是在两个当事人之间私下协商的(换句话说,场外交易OTC)),而期货是在交易所协商的。

期权是两个当事人之间的金融合同。一方(称为期权的持有人)向另一方(称为期权的写入人)支付保险费,以获得权利,但不是义务,以某个特定价格(行权价格)在将来的某个特定日期(到期日)购买某个特定资产(标的物)。这种合同称为欧式看涨期权合同。

例 1

考虑一个标普 500 指数的一个月看涨期权合同。在这种情况下,标的物将是标普 500 指数的价值。在合同成立时(今天,t=0)和到期时(t=T)都有现金流。在合同成立时(t=0),支付保险费,而在到期时(t=T),期权持有人将根据标的物在到期时的价值S(T)选择以下两种可能的情景之一:

  • 情景 A:行使其权利并以K购买标的资产

  • 情景 B:如果到期时标的物的价值低于行权价,即S(T)<K,则不采取任何行动

如果到期时标的价值高于行权价,即S(T)>K,期权持有者将选择 A 方案。这将保证他/她获得S(T)-K的利润。如果到期时标的价值低于行权价,即S(T)<K,期权持有者将选择 B 方案。这将保证他/她将损失限制为零。

例 2

利率互换IRS)是 A 和 B 两方之间的金融合同,他们同意在一段给定的时间内(合同的期限)定期交换现金流。通常,从 A 到 B 的现金流与固定利率挂钩,而从 B 到 A 的现金流与浮动利率挂钩。固定现金流的集合称为固定腿,而浮动现金流的集合称为浮动腿。现金流在合同的期限内(从成立t=0到到期t=T)定期发生。例如,一个固定对浮动的 IRS,每三个月支付一次约定名义N的 5%利率,并每三个月收取约定名义N的 EURIBOR3M。

例 3

股指期货合约也涉及到在合同到期时支付的单一未来现金流(交割价格)。然而,在这种情况下,支付是不确定的,因为我将从这次交易中获得多少利润将取决于到期时标的的价值。

如果标的价格高于交割价格,那么我得到的支付(用函数H表示)是正的(表示利润),对应于到期时标的的价值S(T)和交割价格K之间的差额。如果标的价格低于交割价格,那么我得到的支付是负的(表示损失),对应于交割价格K和到期时标的的价值S(T)之间的差额。这一特征可以总结为以下支付公式:

学科 1 - 金融(金融衍生品)

方程 1

在这里,H(S(T))是到期的支付,这是S(T)的一个函数。金融衍生品对现代金融市场非常重要。根据 2012 年 12 月的国际清算银行BIS)的数据,全球场外衍生品合同的未结算金额为外汇衍生品 67358 亿美元,利率衍生品 489703 亿美元,股票衍生品 6251 亿美元,大宗商品衍生品 2587 亿美元,信用违约掉期 25069 亿美元。更多信息请参见www.bis.org/statistics/dt1920a.pdf

学科 2 - 数学

我们需要数学模型来捕捉标的未来演变和金融衍生品中遇到的有条件现金流的概率特性。

关于有条件现金流,这些可以用我们正在考虑的特定衍生品的支付函数H(S(T))来表示。因为S(T)是一个随机变量,H(S(T))的值应该被计算为期望E[H(S(T))]。为了计算这个期望,我们需要一些技术,允许我们预测或模拟未来的标的S(T)的行为,以便能够计算ST的值,最终能够计算支付的均值E[H(S(T))]

关于标的的行为,通常使用随机微分方程SDEs)来形式化,例如几何布朗运动GBM),如下:

学科 2 - 数学

方程 2

前述方程基本上表示股价的变化(dS)可以理解为两种效应的和—确定性效应(右侧的第一项)和随机效应(右侧的第二项)。参数学科 2 – 数学称为漂移,参数学科 2 – 数学称为波动率S是股价,dt是一个小的时间间隔,dW是 Wiener 过程的增量。

这个模型是描述股票、商品和外汇行为最常见的模型。还有其他模型,比如跳跃模型、局部波动率模型和随机波动率模型,可以增强对基础动态的描述。

关于数值方法,这些方法对应于数学模型中描述的形式表达方式(通常是连续时间)被转换为可以用于计算的近似表示(通常是离散时间)的方式。这意味着描述某些股票指数未来价格演变的 SDE 被改变为在离散间隔描述演变。可以使用欧拉逼近计算 SDE 的近似表示如下:

学科 2 – 数学

方程 3

前述方程需要以迭代方式解决,对于合同现在和到期之间的每个时间间隔。如果这些时间间隔是天,合同从现在起 30 天到期,那么我们可以根据今天的价格计算明天的价格。然后我们可以根据明天的价格计算后天的价格,依此类推。为了定价衍生品,我们需要计算到期时的预期支付E[H(ST)],然后将其贴现到现在。通过这种方式,我们能够计算出欧式期权合同的公平保费学科 2 – 数学

学科 2 – 数学

方程 4

学科 3 – 信息学(C++编程)

C++在定价衍生品中的作用是什么?它的作用是至关重要的。它使我们能够实现解决定价问题所需的实际计算。使用前述技术描述基础动态,我们需要模拟许多潜在的未来情景来描述其演变。比如我们需要定价一年期的 EUR/USD 汇率期货合约。我们必须模拟未来一年每一天的 EUR/USD 的演变(使用方程 3)。然后我们可以计算到期时的支付(使用方程 1)。然而,为了计算预期支付(使用方程 4),我们需要通过一种称为蒙特卡洛模拟的技术模拟成千上万种可能的演变。完成这个过程所需的一系列步骤被称为算法。为了定价衍生品,我们需要构建这样的算法,然后在 C++等高级编程语言中实现它。当然,C++并不是唯一的选择,其他语言包括 Java、VBA、C#、Mathworks Matlab 和 Wolfram Mathematica。然而,C++是行业标准,因为它灵活、快速且可移植。此外,多年来,已经创建了几个用于在 C++中进行复杂数值计算的数值库。最后,C++是一种功能强大的现代面向对象的语言。

在清晰和高效之间取得平衡总是困难的。我们的目标是制作自包含(不太面向对象)和自解释的计算机程序。在公司环境中,当然可以实现更高级的实现,特别是在更大的金融定价库的背景下。在本书中,所有程序都是使用最新的 C++11 标准使用Code::Blocks (www.codeblocks.org) 和 MinGW (www.mingw.org) 实现的。

Bento Box 模板

Bento Box 是日本料理中常见的单人外卖餐。通常,它呈长方形,内部分隔成几个隔间,以容纳构成一餐的各种类型的食物。在本书中,我们使用 Bento Box 的比喻来描述一个视觉模板,以便于、组织和构建衍生问题的解决方案。Bento Box 模板只是一个我们将按顺序填充所需不同元素的表格,以逻辑结构的方式定价衍生品。Bento Box 模板在用于定价特定衍生品时分为四个区域或方框,每个方框包含解决问题所需的关键信息。下图说明了适用于所有衍生品的通用模板:

Bento Box 模板

Bento Box 模板 - 一般情况

下图显示了 Bento Box 模板应用于简单的欧式看涨期权的示例:

Bento Box 模板

Bento Box 模板 - 欧式看涨期权

在上图中,我们填写了各种隔间,从左上角的方框开始,顺时针进行。每个隔间包含有关我们特定问题的细节,从概念(方框 1:衍生合同)到实际(方框 4:算法)的顺序,通过解决问题所需的定量方面(方框 2:数学模型和方框 3:数值方法)。

摘要

本章概述了定量金融的主要元素,应用于定价金融衍生品。Bento Box 模板技术将在接下来的章节中使用,以组织我们解决金融衍生品定价问题的方法。我们将假设我们掌握足够的信息来填写第 1 个方框(衍生合同)。有关数学模型(方框 2)的进一步细节将在第二章 数学模型中描述。

第二章:数学模型

在前一章中,我们将 Bento Box 模板描述为一种用于构建金融衍生品定价方法的方法。在 Bento Box 模板的背景下,本章对应于第 2 个盒子——数学模型。在这里,我们回顾了今天金融衍生品市场中用于描述标的物行为的一些关键数学模型。特别是标的物的未来演变。以下是这些标的物的例子:

  • 股票或股票

  • 汇率

  • 利率

  • 信用评级

股票

在股票资产类别中,标的物是公司股票的价格。例如,伦敦证券交易所(www.londonstockexchange.com)上 Vodafone PLC(VOD.L)一股的当前价格在某个特定时间。价格可能是£2.32,时间可能是 2013 年 5 月 13 日 11:33:24。

从数学角度来看,股票价格可以表示为当前时间t的标量函数。我们将这个函数表示为S(t)。请注意,在技术术语中,S(t)是一个时间序列,尽管表面上是连续的(具有C[0]连续性),但实际上是不连续的(存在跳跃)。此外,它不是一个行为良好的函数,也就是说,它的一阶导数不存在。

我们将S(t)建模为随机变量。我们围绕这个值构建的所有构造,比如支付的价值H(S_t),都将是随机函数。在这种情况下,我们不需要使用标准的微积分工具(如泰勒级数、导数、黎曼积分),而是需要使用随机微积分工具(如伊藤引理、Radon-Nykodym 导数、黎曼-斯蒂尔切斯积分)来推进我们的建模。

在这种情况下,变量S(t)的行为可以用 SDE 描述。在股票的情况下,用于描述股票行为的标准 SDE 称为GBM。在所谓的实际概率测度P下,GBM 在连续时间中形式上表示如下:

Equity

方程 1

然而,在文献中,这种表示法并不用于金融衍生品的定价。它被风险中性测度Q下的以下表示法所替代:

Equity

方程 2

在前面的方程中,我们用无风险利率Equity替换了漂移EquityEquity是波动率,dW是 Wiener 过程的增量。方程 2 可以进一步表示如下:

Equity

在前面的方程中,我们可以将方程的左手边(LHS)的dS/S项识别为股票的回报。因此,方程的右手边(RHS)的两项是“漂移项”和“波动率项”。这些项都由参数EquityEquity“缩放”,这些参数是根据交易工具的当前市场价格进行校准的,比如认购和认沽期权。

请注意,方程 2 是用于描述金融衍生品中标的物的基本方程。

为了定价衍生品,我们需要将方程 2 从连续时间转换为离散时间,以模拟股票的行为,比如每天(Equity天,但在金融中我们总是以年化的方式工作,所以Equity年)。我们可以通过使用 Euler-Murayama 离散化来轻松地近似方程 2,如下所示:

EquityEquityEquity

方程 3

在上述方程中,我们将 Wiener 过程的微分近似为 delta t的平方根乘以一个均值为零、标准差为 1(N(0,1))的高斯分布抽样。方程 3 是一个线性迭代方程,我们可以通过具有S0的初始值计算出一系列时间步长S1S2、…、SN。我们只需要参数rσ的值,以及一个高斯随机数生成器来得到ε的值。

为了计算从累积标准正态分布中抽取的值,我们将使用一种称为Box-Muller方法的方法。Box Muller 方法允许我们将均匀随机数转换为高斯随机数。这非常有用,因为在许多计算机库中,我们可以找到从均匀分布生成随机数的标准函数(例如,在 C 中的函数rand()),通过 Box Muller 方法,我们可以生成所需的高斯抽样。我们将在第四章中更多地讨论这个问题,C++中的股票衍生品

例如,假设我们想要模拟公司 ABC 未来四天的股票行为。目前股票的价值为 100 欧元。无风险利率为年化 5%,波动率为年化 30%。我们应该如何进行?

首先,我们为我们需要值的工作日构建一个时间网格(请注意,一年中有 255 个工作日)。这些是t0t1t2t3t4。这些分别对应于星期一、星期二、星期三、星期四和星期五。在金融领域,我们总是以年化的方式工作,因此这些日期对应于股权,因此t0=0t1=1/255t2=2/255t3=3/255t4=4/255

然后,我们需要股票价格S(t)的网格。这些分别是S(t0)S(t1)S(t2)S(t3)S(t4),我们将分别分配给星期一、星期二、星期三、星期四和星期五,表示为S0S1S2S3S4。我们已经知道S0的值,即初始价格(如今观察到的),即星期一的S0=100

在这之前,我们需要从累积标准正态分布中得到一组向量,如下所示:

股权

然后,我们可以通过以下方式迭代地应用方程 3,从星期一的价值S0到星期二的价值S1

股权

或者,我们可以通过以下数值值从星期一的价值S0到星期二的价值S1

股权

然后我们可以按以下方式计算其余几天的值:

股权股权股权

如果我们将这组计算出的股票价格放在一起,并将它们绘制在时间轴上,我们将得到以下图表:

股权

模拟股票价格

外汇

在外汇资产类别中,标的是汇率的价值。例如,在某个特定时间,欧元(EUR)和英镑(GBP)之间的当前汇率。汇率可能是EUR/GBP = 1.31,意味着 1 英镑将被 1.31 欧元兑换,当前时间可能是 2013 年 5 月 13 日 11:33:24。

因此,在数学术语中,汇率可以表示为时间的标量函数X(t),就像股票一样。汇率X(t)因此被建模为随机变量。在数学术语中,X(t)的行为使用 SDE 描述,就像股票一样。然而,对于股票,我们使用了 GBM,而在外汇交易中,我们将使用一种来自(Garman-Kohlhagen 1983)的变体。根据这个模型,汇率的随机微分方程可以表示如下:

外汇

方程 4

在上述方程中,rdrf代表国内和外国无风险利率。波动率外汇交易是校准到市场报价工具的参数。

与之前一样,在进行衍生品定价之前,我们需要将方程 1 从连续时间转换为离散时间,以模拟每天的汇率行为(外汇交易天)。

我们再次将方程 1 应用 Euler-Murayama 离散化,将微分dX转换为差异外汇交易,保持常数rdrf外汇交易不变,并将 Wiener 过程的微分近似为 delta t的平方根乘以从累积标准正态分布中得出的随机数,如下所示:

外汇交易外汇交易外汇交易

方程 5

与方程 2 一样,方程 5 也是一个线性迭代方程,我们可以通过具有一系列时间步长X1X2,…,XN的起始值X0来迭代计算。我们只需要参数rdrfsigma的值以及一个高斯随机数生成器来获得epsilon的值。

例如,假设我们想要模拟 EUR/USD 汇率在接下来的四天内以最后一个交易日的报价值(称为日终EOD))的行为。EUR/USD 的当前汇率为 1.33。国内无风险利率为 5%每年,外国无风险利率为 3%每年,波动率为 30%每年。我们应该如何进行?

首先,我们构建一个时间网格,用于我们需要数值的工作日(一年有 255 个工作日)。这些是t0t1t2t3t4。这分别对应星期一、星期二、星期三、星期四和星期五,对应的是t0=0t1=1/255t2=2/255t3=3/255t4=4/255,以年化的方式。

然后我们需要 EOD 汇率网格X(t)。这些是X0X1X2X3X4。我们已经知道X0的值,即初始外汇率(今天观察到的),即星期一的X0=1.33。与之前一样,在继续之前,我们计算从累积标准正态分布中得出的一组随机数,得到以下结果:

外汇交易

我们可以通过以下方式迭代应用方程 3,从星期一的值X0到星期二的值X1

外汇交易

或者,我们可以通过以下数值值从星期一的值S0到星期二的值S1进行计算:

外汇交易

然后我们可以按以下方式计算其余几天的值:

外汇交易外汇交易外汇交易

如果我们将这些计算出的利率集合放在一起,并将它们作为时间的函数绘制出来,我们会得到以下图表:

外汇交易

模拟汇率

利率

在利率资产类别中,基础是利率。利率非常复杂。如果我们考虑问题“今天的利率是多少?”,我们会发现答案肯定不仅仅是“5%每年”,因为我们还需要指定我们想要知道的利率的到期日(T)。因此,利率比股票或外汇等物品多了一个维度。当前观察到的股票价值是一个标量数量,即一个单一数字,而当前利率曲线是一个向量。

例如,让我们考虑 2013 年 5 月 13 日观察到的现货 EURIBOR 利率,期限为 1 个月、3 个月、6 个月和 12 个月(由www.euribor-ebf.eu/发布)。我们将这些现货利率标记为R(t,T),如R(0,3M)=EURIBOR 3M = 1 percent paR(0,6M)= EURIBOR 6M = 2 percent paR(0,9M)=EURIBOR 9M = 3 percent paR(0,12M)=EURIBOR 12M = 4 percent pa。请注意,t=0,因为我们考虑 2013 年 5 月 13 日为当前日期。

这些利率将如何在未来发展?换句话说,我们如何对R(t,T)进行建模?我们有以下两种选择,反映了文献中存在的两种建模学派。

  • 短期利率模型

  • 市场模型

第一个是最古老的,而第二个是更近期的。

在第一个模型中,关键建模变量是利率的理想化,即所谓的短期利率。这是一个微不足道的利率dr,适用于非常短的时间间隔。要获得适用于整个期间的利率,我们应该增加或积累该期间内所有这些小利率的影响。在第二个模型中,关键建模变量是实际报价或市场利率,如 LIBOR。这就是为什么这些模型通常被称为市场模型,其最著名的版本被称为Libor 市场模型

短期利率模型

在连续时间中,短期利率可以由 Vasicek 开发的以下 SDE 表示:

短期利率模型

方程 6

前述方程是一个均值回归过程。参数短期利率模型是均值回归水平,短期利率模型是均值回归速度,短期利率模型是波动率。参数短期利率模型短期利率模型短期利率模型控制了随机过程的行为。分配给短期利率模型的值将是利率将趋向的长期利率水平,而短期利率模型将控制利率返回到长期均值水平的速度。波动率短期利率模型控制了过程的“跳跃”幅度。这些参数可以校准到市场报价的工具,如利率互换期权(称为swaptions)。

我们可以通过之前描述的 Euler-Murayama 方法来近似 Vasicek 过程,以获得随机过程的离散化版本,如下所示:

短期利率模型短期利率模型短期利率模型

方程 7

前述方程是一个线性迭代方程,我们可以通过对r0的起始值进行迭代计算一段时间步骤r1r2、…、rN来计算。我们只需要参数thetalambdasigma的值,以及用于epsilon值的高斯随机数生成器。

例如,假设我们想要模拟未来四天的利率短期行为。当前利率为 5%。参数短期利率模型=1.0 和短期利率模型=2.0,而波动率为 30%。我们应该如何进行?

首先,我们为需要数值的日期构建一个时间网格。这些是t0t1t2t3t4,以便对应于星期一(t0)、星期二(t1)、星期三(t2)、星期四(t3)和星期五(t4),依次对应于t0=0t1=1/365t2=2/365t3=3/365t4=4/365的年化期限。

我们接下来需要短期利率r(t)的网格。这些是r0r1r2r3r4。我们将它们分别分配给星期一、星期二、星期三、星期四和星期五。由于我们已经知道了r0的值,即初始利率(如今天所观察到的),我们有星期一的r0=5%

与之前一样,在继续之前,我们计算从累积标准正态分布中抽取的向量,以获得以下结果:

短期利率模型

然后,我们可以迭代地应用方程 3,从星期一的值r0到星期二的值r1,如下所示:

短期利率模型

或者,我们可以通过以下数值值从星期一的值S0到星期二的值S1

短期利率模型短期利率模型

然后我们可以按如下方式计算其余日期的值:

短期利率模型短期利率模型短期利率模型

市场模型

Libor 市场模型LMM)是一种用于定价利率衍生品的高级数学模型。LMM 也被称为 BGM 模型,以其作者(Brace, Gatarek, Musiela, 1997)命名,已经成为全球金融市场的霸主。文献中提供了大量关于 LMM 的出版物,主要涉及其许多变体和复杂的高级问题。

实际上,LMM 不能被理解为单一模型,而应被理解为一个庞大的模型家族(Rebonato 1998)和(Brigo and Mercurio 2006)。它的许多变体包括考虑的因素数量、使用的波动率建模类型、使用的相关性建模类型、是否使用随机波动率或 SABR、是否使用远期 LIBOR 利率或掉期利率,以及使用半解析或数值解方法等。

我们的方法和符号紧随(Pelsser 2000)的方法,尽管简洁,但清晰地介绍了 LMM。在所有可能的 LMM 变体中,我们选择了最简单的实现方式——即使用对数正态 SDEs(GBM)来描述远期利率,以及一个驱动所有利率波动性的单一 Wiener 过程(即单因素情况)。在这些条件下,我们进一步探讨了平稳波动性的使用。

我们首先将利率期限结构N分为一组远期利率L和一组重置时间T,如下所示:

市场模型

前述每个远期利率将有其自己的随机过程驱动,这将导致N个随机过程。按照 BGM,我们使用几何布朗运动(BGM)来描述每个这些随机过程,如下所示:

市场模型

方程 8

我们现在进一步简化模型,使用一个驱动所有远期利率的单一因素。这种简化后来可以放宽为多因素 LMM。可以证明,通过选择最后一个利率作为终端度量,漂移的形式如下:

市场模型

请注意,前述 GBM 方程中的漂移是远期利率的函数,因此不是恒定的,而是依赖于状态。

鉴于 LMM 中的过程的复杂性,不可能为所有远期利率获得封闭形式的解。然而,这并不是问题,因为可以使用强大的数值方法来解决远期利率的离散化版本。广泛用于市场模型的一个重要方法是蒙特卡洛模拟。远期利率Li(Tn)是即期 LIBOR 利率的实现。在每一列中,使用以下离散化来更新远期利率Li(Tn+1)

市场模型

方程 9

前述方程,就像我们为股票和外汇展示的方程一样,应该通过模拟迭代求解。远期利率可以按如下的算术表排列:

市场模型

在上表中,左列代表t=0时的利率期限结构。给定在T1T2、…TN上实现的一组 LIBOR 利率,我们可以提取我们需要模拟的未来利率,即在上表的主对角线上的利率。

例如,考虑以下支付固定利率换浮动利率利率互换IRS),名义金额为 100 万欧元。该 IRS 每 3 个月支付 5%,并每 3 个月收取 EURIBOR 3 个月利率。该互换的总到期日为一年,根据以下当前利率期限结构:

市场模型

使用 LMM 计算该互换的现值。我们应该如何进行?

t=0时的利率期限结构为EURIBOR 3M = 1%EURIBOR 6M = 2%EURIBOR 9M = 3%EURIBOR 12M = 4%

首先,我们需要通过从t=0时观察到的拔靴法计算初始远期利率。我们使用以下方程获得这一点:

市场模型

然后使用迭代方程 9,我们计算未来的远期利率如下:

市场模型

我们将初始远期利率填入最左侧列,然后逐列向右移动,直到获得所需的所有值,如右侧所示。有关计算的更多细节,请参阅(Pelsser 2000)的书籍。请注意,即使 IRS 可以“静态”定价(即,无需模拟),我们使用此示例来说明 LMM 方法计算所需的步骤。

信用

在信用衍生品建模中,基础是信用风险。现代信用风险测量方法可以分为两种替代方法——由(默顿 1974 年)开创的结构方法和利用基于强度的模型估计随机危险率的简化形式方法,由包括(贾罗和特恩布尔 1995 年)、(贾罗、兰多和特恩布尔 1997 年)和(达菲和辛格尔顿 1999 年)在内的各种作者开创。

结构模型

信用风险的结构方法假设公司资产的市场价值低于其必须支付的义务或债务时,公司会违约。因此,结构模型有时也被称为资产价值模型。这些模型查看公司的资产负债表和其资本结构,以评估其信用价值。然而,这种方法的一个关键问题是,公司资产的价值很难直接观察到。年度报告只提供公司真实资产的会计版本,而不是它们的市场价值。对于上市公司,股本通常是可观察的,债务也是如此。 (默顿 1974 年)从以下形式的极其简化的资本结构假设开始:

结构模型

方程 10

在上述方程中,V代表公司的价值(公司资产的总和),E是其股本,D是其债务。股本E被理解为公司股本的总价值,等于股票的市场价值乘以市场上的股票数量。对于这家简化的公司,债务由一张到期日为T的零息债券代表。

此时,默顿提出了一个问题:“对于具有前述资本结构的公司,何时会违约?”嗯,这取决于我们对违约的定义。如果我们将公司无法在某个特定未来时间T支付其债务视为违约,那么如果公司在T时的价值V(T)大于债务的面值D(T),则满足此条件。此时,债券持有人将要求支付,公司将有能力支付。相反,如果在到期时,公司的价值小于其需要支付的债务价值,那么它将无法履行其义务,并将违约。这两种情况可以用数学方式定义如下:

结构模型

股东怎么样呢?他们最终拥有公司的股票。考虑前两种情况,我们知道如果公司违约,他们什么也得不到,而如果公司继续运营,他们在到期时收到V(T)D(T)之间的差额,因此得到以下方程:

结构模型

请注意,左侧的表达式可以简洁地写成右侧的单个表达式。右侧的表达式代表到期时股东的回报。此外,这个表达式与标的资产V(t)和行权价D的欧式看涨期权的回报具有完全相同的形式。根据默顿的假设,我们进一步假设公司的动态遵循 GBM 如下:

结构模型

方程 11

通过这样做,我们建立了信用风险和股票衍生品定价之间的桥梁。事实上,我们现在可以使用所有股票衍生品定价的结果来定价信用风险的结构模型。请注意,波动率 结构模型 是公司资产的波动率,而不是股权波动率。因此,公司在t=0时的资产价值可以使用 Black-Scholes 公式计算如下:

结构模型

方程 12

结构模型

在方程 12 中,N()是累积标准正态分布。当我们需要计算诸如公司的首次公开发行IPO)等问题时,这是一个有用的表达式,并且可以根据公司的特征确定其股权的公平价格。

例如,假设我们有一家公司,其资产总值为 1 亿美元。无风险利率为 5%。假定公司资产的波动率为 20%。其债务面值为 7000 万美元,将在四年内作为零息债券支付。在t=0时,其股权的公允价值应为多少?

我们使用信用风险结构模型的框架,并按以下步骤进行。参数为 结构模型, 结构模型, 结构模型, 结构模型, 和 结构模型。使用前述公式,我们得到以下方程:

结构模型结构模型结构模型

强度模型

违约是一个随机变量τ(τ),表示公司将违约的时间。违约可以描述为破产、支付不足等。为了模拟信用事件的到达风险,我们需要对时间τ进行建模,这是一个未知的随机时间点。

基于强度的模型直接关注描述违约的条件概率,而不是确切的违约事件的定义。强度模型基于存活概率的概念。这是从精算和生物科学中借鉴的一个想法。存活概率是一个衰减的指数函数,描述了公司或国家(主权国家)存活的概率。这可以用最简单的数学术语来表达如下:

强度模型

方程 13

但我们想要建模违约,而不是存活。事实上,我们可以计算在未来某个期间[S,T]内的违约概率,如从当前时间t所示,简单地作为两个连续存活概率之间的差异。那么在那个未来期间内的违约概率强度模型可以使用以下公式计算:

强度模型

方程 14

上述公式对于建模信用风险至关重要。有了它们,我们可以描述违约的可能性,这是未来会发生的一个概率事件。与这一未来事件相关的现金流因此取决于这一事件的发生。遵循金融衍生品的经典框架,因此信用衍生品可以被理解为有条件的索赔,其中触发未来现金流的事件是违约。

具体来说,考虑一个简化的情况——我们期望在未来某个时间T从公司那里收到一笔现金流。这笔现金流的现值是多少?

如果我们不考虑信用风险,现金流的现值(PV)就是现金流的贴现未来值(FV)。如果在中间时间的贴现因子是 DF,那么我们可以写出金融中最简单的公式,如下所示:

强度模型

我们可以再进一步,假设连续复利和恒定无风险利率r,我们可以得到以下方程:

强度模型

方程 15

现在如果我们考虑公司的信用风险会发生什么?首先要注意的是现金流不再是确定的,而是不确定的。在数学术语中,它不再是确定性的,而是概率性的。因此,我们应该重新书写前面的方程。但因为未来现金流是未知的,实际上是一个随机变量。我们应该将它写成如下随机变量的期望值:

强度模型

这个期望引入了未来发生或不发生 FV 的概率。但这个概率是什么?这个公司将来能否做出支付的概率。换句话说,公司在那时候存活的概率。因此,我们可以用强度模型代替强度模型以得到以下公式:

强度模型

此外,我们可以明确表示前面描述的存活概率如下:

强度模型

只有在未来公司存在(或存活)时,我们才会收到未来的现金流。考虑信用风险后,现金流的现值的最终表达式如下:

强度模型

方程 16

最后一个表达式很有趣,因为它显示了信用风险是一种“利差”,它被添加到我们通常用来贴现未来现金流的无风险利率中。

例如,假设我们期望从一个具有信用风险的交易对手那里收到 100 万美元,其风险由其危险率量化为强度模型。假设无风险利率为强度模型,首先计算现金流的现值,没有信用风险,其次是有信用风险。假设连续时间贴现。假设追偿率为强度模型。我们应该如何进行?

通过应用前述公式,我们得出没有信用风险时PV如下:

强度模型强度模型

现在,如果我们考虑信用风险,我们有以下的PV

强度模型强度模型

请注意第二笔现金流小于第一笔。信用风险的影响是将现金流的价值(其PV)视为降低,因为不确定,必须乘以它发生的机会(即其存活概率)。

在先前的分析中,我们假设当发生违约时,所有未来现金流 FV 都将丢失。然而,在大多数实际情况下,会有一部分资金得到追回。如果我们将这一部分定义为追偿率R,有风险未来现金流的现值PV仍然是相同的,如下:

强度模型

但是,期望现在以违约或不违约的两种可能状态来解决,每种状态都乘以其相应的概率,如下:

强度模型

在前述方程中,我们考虑了违约情况下的追偿率R。有风险现金流PV的现值现在如下:

强度模型

总结

本章概述了用于定价现代金融市场衍生品的基本模型。我们现在将以这些模型为基础,对我们审查的各种资产类别的基础进行建模,并应用一些数值方法,以在计算机中高效地实施它们的计算。

在下一章中,我们将集中讨论数值方法。

第三章:数值方法

在上一章中,我们回顾了一些用于描述金融衍生品基础资产行为的关键数学模型。特别是,我们看到了这些模型如何用于根据我们今天拥有的信息描述这些资产的未来行为。这些模型通常用 SDEs 和偏微分方程(PDEs)来表示。

在本章中,我们将描述当今金融市场上用于金融衍生品的三种主要数值方法。它们是一种将实际数值应用于我们在上一章中看到的抽象数学公式的方法。这些数值方法如下:

  • 蒙特卡洛MC)模拟

  • 二叉树BT

  • 有限差分方法FDM

在 Bento Box 模板的背景下,本章对应于第 3 个盒子——数值方法。还有一种不太常用的第四类方法,称为积分方法,用于数值积分。这些不会在这里讨论。

蒙特卡洛模拟方法

蒙特卡洛模拟是以摩纳哥公国著名的赌场命名的。由于其简单性、灵活性和可扩展性,它是工业中用于定价金融衍生品的最广泛使用的数值方法。

该方法的基本思想是构建一个模拟引擎,使我们能够预测基础资产未来可能发展的多种方式(或轨迹)。这些轨迹可以被视为潜在的经济或金融场景。通过蒙特卡洛模拟,我们试图回答诸如“鉴于今天的沃达丰股票价格,未来一个月内每天的股票价格可能是多少?”这样的问题。

由于我们无法确定价格的未来演变,我们的结果需要基于概率,因此我们需要大量的样本。使用我们在上一章中看到的随机模型来模拟一个可能的轨迹,通过蒙特卡洛模拟,我们将模拟许多可能的轨迹,并且对于每一个,计算合同如果价格按照未来的特定路径发展的话将会有的回报。然后,我们将取所有这些可能的回报并计算它们的期望值,即平均值。这将给我们一个估计,即这份合同在未来将价值多少。

蒙特卡洛模拟随后允许我们计算金融衍生品的公平价格,即其预期贴现回报。这个概念源自公平定价的金融原则,即合同应该具有的价格,如果我们预期收到的现金流总和与我们预期支付的现金流总和相同。有关蒙特卡洛模拟的更多细节,欢迎参考《金融工程中的蒙特卡洛方法》。

为了直观地理解为什么会这样,考虑以下简单的例子:

想象一下,您在t=0时购买了一个普通的欧式看涨期权合同。该合同将在到期日t=T给您一笔回报!因为到期时的基础价值是不确定的,即S_T是一个随机变量,回报函数H(S_T)也是不确定的。我们可以写出回报函数的期望值!此外,在欧式看涨合同中,我们今天支付保险费,以便在到期日t=T行使期权或不行使期权。我们今天应该为这份合同支付多少保险费?

正如我们之前所说,在公允价值设置中,我们期望收到的应该等于我们期望支付的。通过将所有这些现金流合并在一起(正数表示应收,负数表示应付),我们可以写成以下形式:

  • t=0时支付的金额写为蒙特卡洛模拟方法

  • t=T时应收金额写为蒙特卡洛模拟方法

如果我们现在计算这些现金流的现值,我们得到以下方程:

蒙特卡洛模拟方法

换句话说,可以总结如下:

蒙特卡洛模拟方法

MC 模拟方法的目的正是帮助我们计算支付的期望值蒙特卡洛模拟方法;一旦计算出这个值,就将其折现以获得衍生品的保费。

这个想法也可以推广到更复杂的设置,包括许多复杂的支付和标的资产。

MC 方法的算法

对于欧式衍生品,MC 模拟包括以下三个步骤:

  1. 第一步是生成轨迹。

模拟从t=0到到期t=T的标的资产的* M条轨迹。在这一步中,我们使用描述标的资产演变的 SDE 的离散化版本。在我们的情况下,我们使用 GBM 作为 SDE,这将允许我们将股票的价值从当前价值S_0取到到期时的价值t=T。离散化版本本质上是一个适用于有限时间步长而不是连续时间步长的近似版本。有关更多细节,请参考《金融工程中的蒙特卡洛方法》。我们将期权合同的寿命离散化为N步,每步大小为dt*,可以简洁地写成如下形式:

MC 方法的算法

在这一步结束时,我们应该得到一个S_T的值向量,如下所示:

MC 方法的算法

这些代表了在t=TS的潜在价值的一组可能情景。我们使用 GBM 生成多条路径,这些路径将用于预测到期时S_T的价值。

  1. 下一步是计算期望。

一旦我们得到到期时标的资产的价值集合,现在需要计算到期时支付的期望。因此,我们取这些价值中的每一个,并按如下方式计算每个价值的支付:

MC 方法的算法

前述方程将给我们一个支付向量。为了计算期望,我们只需要简单地取支付的平均值,如下所示:

MC 方法的算法

  1. 现在将期望值折现到现在。

最后一步是将到期时的支付价值折现到现在。为了做到这一点,我们将使用以下公式:

MC 方法的算法

或者,我们也可以使用连续复利,如下所示:

MC 方法的算法

前述方程将给我们衍生品的价值MC 方法的算法。请注意,在这种情况下,我们假设利率和股票价格之间没有相关性。这就是为什么我们可以在前述方程中清晰地分离这两种影响。如果利率和股票价格相关,那么我们将无法分离贴现因子和期望。这种无相关性的假设是简单定价模型的标准。

MC 方法的示例

考虑一个例子,我们想要定价沃达丰股票(VOD.L)的六个月欧式看涨期权。沃达丰的当前股价是£100.00,波动率为 20%,行权价为£100。我们假设股票不支付股息。当前无风险利率为 5%。我们如何使用蒙特卡洛模拟来解决这个问题?我们通过以下三个阶段进行:

  1. 第一步是生成轨迹。

我们应用 GBM 来模拟从今天的现货价格S_0 = £100.00开始的 VOD.L 股票价值。为简单起见,我们选择将期权的生命周期从t=[0,T]离散化为N=5个时间步,并使用离散的 GBM 进行M=5次模拟,如下所示:

蒙特卡洛方法的示例

五条轨迹将如下所示:

蒙特卡洛方法的示例蒙特卡洛方法的示例蒙特卡洛方法的示例蒙特卡洛方法的示例蒙特卡洛方法的示例

到期时的股票价格将如下所示:

蒙特卡洛方法的示例

  1. 下一步是计算期望。

对于每个标的物的价值,我们现在计算如下的支付:

蒙特卡洛方法的示例

现在我们使用特定形式的支付来描述欧式看涨期权如下:

蒙特卡洛方法的示例

我们将以下数字应用于前述方程,得到以下结果:

蒙特卡洛方法的示例

前述计算的期望值如下:

蒙特卡洛方法的示例

  1. 现在将期望贴现到现在。

现在我们使用以下连续复利来贴现我们刚刚在步骤 2 中计算的期望支付,以确定保费的价值:

蒙特卡洛方法的示例

在这个例子中,我们只使用了五种情景来计算我们的蒙特卡洛价格。实际上,为了得到可接受的误差,需要数百甚至数千种情景。显然,使用的情景越多,逼近的精度就越高。可以推导出一些蒙特卡洛方法的误差界限公式,并展示收敛速度。有关更多细节,请参考《金融工程中的蒙特卡洛方法》。将五个蒙特卡洛情景的所有轨迹放在一起,我们得到了下面截图中显示的表格。在这里,我们看到所有的轨迹都从S0=100开始,导致一些最终值S5。对于每条轨迹,我们计算H的支付,然后对其进行平均以计算其期望值。然后将结果打折以获得衍生品的现值。

蒙特卡洛方法的示例

蒙特卡洛模拟的示例

二项树方法

二项树BT)可以追溯到(Cox,Ross 和 Rubinstein 1979)的工作。与 MC 方法一样,它们基于股票价格的离散化如何可以向上或向下跳跃的想法。与 MC 方法不同,BT 不是基于模拟许多可能的路径,而是基于构建一个在每个节点分叉的可能未来价格的单一路径。这些价格以及它们的相关概率构成了树。一旦建立了这棵树,就可以确定到期时的标的物价格,并且可以计算到期时的支付,并将其贴现到现在的时间,以确定衍生品的保费。

BT 方法的算法

当应用于定价衍生品时,BT 方法由三个阶段组成:价格树的构建(前向阶段)、支付的计算(到期阶段)和支付的贴现到现在的时间(后向阶段)。我们现在将在一个两步 BT 的简化环境中解释 BT 方法。这可以很容易地推广到N步树。

首先,我们假设标的物在下一个时间步只能上升或下降。因此,我们指定上升因子u来描述今天的价值如何变化为更高的价值,下降因子d来描述今天的价值如何变为更低的价值,使得上升值为S(T)= u S(0),下降值为S(T)= d S(0)。此外,参考“期权定价:一种简化的方法”。上升和下降值的公式如下所示:

BT 方法的算法

以下是上升概率的公式:

BT 方法的算法

下降的概率是BT 方法的算法。现在我们可以继续在以下三个阶段构建我们的二叉树:

  1. 第一阶段是向前阶段。

在这里我们构建树。与 MC 模拟一样,时间从t=0t=T以步长dt离散化。从一个步骤tp,标的物的下一个价格可以根据以下公式中的因子ud上升或下降:

BT 方法的算法BT 方法的算法BT 方法的算法

因此,到期时树的价值如下:

BT 方法的算法

在一般情况下,我们以类似的方式进行,直到到达到期日T,变量SN+1个值。我们将利用以下方程计算这些值:

BT 方法的算法

在我们的情况下,前面的方程可以总结如下:

BT 方法的算法

整个过程如下图所示:

BT 方法的算法

  1. 第二阶段是支付阶段。

在这个阶段,我们使用到期时的标的物价值,对于每个价值,我们计算支付的价值如下:

BT 方法的算法

在我们的情况下,方程可以总结如下:

BT 方法的算法

接下来是到期时期权的价值T

BT 方法的算法

在我们的情况下,前面的方程可以总结如下:

BT 方法的算法

  1. 第三阶段是向后阶段。

在最后阶段,我们取到期时的支付价值,并向后进行。我们通过计算期望的折现支付在前一节点中的加权概率来从最后一个节点移动到前一个节点,计算期权价值如下:

BT 方法的算法

在我们的情况下,在第二步,方程如下:

BT 方法的算法BT 方法的算法

而且,在第一步,方程如下:

BT 方法的算法

衍生品的保费,期权价格,是值BT 方法的算法

BT 方法的示例

考虑一个例子,我们想要定价劳斯莱斯(RR.L)股票的六个月欧式看涨期权。股票的当前股价为£100.00,年波动率为 30%,行权价为£90。我们假设股票不支付股息。当前无风险利率为 5%。我们如何使用 BT 解决这个问题?

首先,我们将期权的生命周期分为两个步骤,因此dt=0.25。以下截图中的表格说明了应用于此问题的三个步骤的数值值:

BT 方法的示例

二叉树定价的示例。

我们首先计算上升和下降因子以及上升概率p。在数值上,这些是使用以下方程计算的:

BT 方法的示例

以下分别是上升和下降的概率:

BT 方法的示例BT 方法的示例

有了所有这些参数,我们现在可以按照以下三个阶段构建我们的树:

  1. 第一阶段是向前的阶段。

现在我们可以按照以下方式构建树的两个层次。

BT 方法的示例BT 方法的示例BT 方法的示例

因此,到期时树的值如下:

BT 方法的示例

  1. 第二阶段是支付阶段。

在这个阶段,我们使用到期时的标的物价值,对于每个标的物价值,我们计算支付的价值,如下:

BT 方法的示例

在我们的情况下,方程可以总结如下:

BT 方法的示例

接下来依次是到期时期权的价值T

BT 方法的示例

在我们的情况下,前面的方程可以总结如下:

BT 方法的示例

  1. 第三阶段是向后的阶段。

在最后阶段,我们取到期时的支付价值,并向后进行。我们通过计算在先前节点中以加权概率折现的预期支付来从最后一个节点移动到先前的节点,如下:

BT 方法的示例

在我们的情况下,在第二步,方程如下:

BT 方法的示例BT 方法的示例

而在第一步中,方程如下:

BT 方法的示例

有限差分法

有限差分(FD)方法是一种数值技术,直接关注微分方程的近似解。正如(Black and Scholes 1973)所示,对于股票金融衍生品(有条件的索赔),问题是用偏微分方程PDE)来表达的。

FDM 的基本思想是离散化微分方程。该方法将微分方程中的导数转化为近似导数的量或比率。这些量不再是无穷小的,而是有限的,即它们有一个有限的长度。这就是有限差分名称的由来。有关更多细节,读者可以参考《金融衍生品的数学:学生导论》。

考虑以下图示,其中连续函数f(X)和函数的一阶导数定义如下:

有限差分法

前面的函数也被称为斜率,它是函数相对于步长dx的增长(或减少)的比率。使用前面的有限差分允许我们计算f(x)函数的斜率,用代数量表示。

在量化金融中,我们遇到各种类型的 PDE。最重要的是 Black-Scholes PDE,它表示如下:

有限差分法

我们现在考虑在St轴上的矩形域中解决这个方程。在S轴上,域是[a,b]。在t轴上,域是[0,T]。这可以用数学表示为域有限差分法。对于欧式看涨期权,它有以下最终条件:

有限差分法

以下是边界条件:

有限差分法

我们不是直接解决 Black-Scholes PDE(即使用变量St),而是按照(Wilmott et al. 1995)的方法,我们将提出一个变量的改变。这将把原始 PDE 转化为一个等价的更容易解决的 PDE,实际上是热扩散的经典方程。变量的改变如下:

有限差分法有限差分法

前述方程将 Black-Scholes PDE 转换为热扩散的经典方程,如下所示:

有限差分法

欧式看涨期权的回报被转换成以下方程:

有限差分法

其中参数k为:有限差分法

FDM 算法

将 FDM 应用于前述 PDE 需要对时间的一阶导数和对x的二阶导数,得到以下方程:

FDM 算法FDM 算法

前述的近似可以从泰勒级数展开中得出。参见(Wilmott 等人,1995 年),就像我们在前一节中所做的那样。

为了做到这一点,我们需要将函数的域离散化为一组离散节点。在 BS 方程的情况下,空间维度将有N个划分(或N+1)节点,时间维度将有M个划分(或M+1)节点。

如果我们现在将前述的近似放在一起,我们将得到以下公式:

FDM 算法

解出前述方程左侧的项,最终得到 PDE 的离散版本如下:

FDM 算法

在前面的方程中FDM 算法

PDE 的离散版本可以通过时间迭代求解,使用显式前向有限差分法(FDM),因为这是期权定价的有限差分技术的最简单实现。我们现在准备按照以下步骤应用 FDM,如下所示:

  1. 首先,将域离散化。

在空间和时间维度中执行此步骤,时间步长为FDM 算法

  1. 现在用有限差分逼近每个导数。

就像我们在前一节中所示的那样,我们将应用将 PDE 的连续导数转换为有限近似的原则。这种有限近似将导致代数方程。在文献中,这组方程被称为模板

  1. 接下来将模板放置到域的所有节点上。

我们现在将模板应用于域中的所有节点,但不包括代表初始和边界条件的节点。对于这些节点,我们知道值是先验的,因此不需要计算。

  1. 在时间上使用模板迭代解决,直到覆盖整个域。

在显式 FDM 中,您只需推进并计算未知函数u的值。请注意,在其他形式的 FDM(如隐式 FDM)中,我们需要通过矩阵问题解决一组方程。有关隐式方法的更多细节,请参阅(Wilmott 等人,1995 年)。

FD 方法示例

考虑一个例子,我们想要定价巴克莱股票(BARC.L)的六个月欧式看涨期权。BARC 的当前股价为 75 英镑,年波动率为 30%,行权价为 75 英镑。我们假设股票不支付股息。当前无风险利率为 5%。我们如何使用 FDM 解决这个问题?

我们知道,当股票使用 GBM 进行建模时,股票金融衍生品满足 Black-Scholes PDE。因此,我们解决了我们在前一节中描述的热扩散方程。就像我们之前做的那样,我们将应用以下四个阶段来解决我们的 FDM 问题:

  1. 首先将域离散化。

我们将域划分为N个空间划分dSM个时间划分dt,因此N=5M=4。我们首先在空间和时间维度中应用这些值,时间步长为FD 方法示例

因此,我们得到六个时间点如下:

FD 方法示例

空间上的五个点如下所示:

有限差分方法的示例

  1. 现在用有限差分逼近每个衍生品如下。有限差分方法的示例在前面的方程中,有限差分方法的示例

  2. 将图案对准域的所有节点。

以下是初始条件:

有限差分方法的示例

或者,以下是具有数值的条件:

有限差分方法的示例在前面的方程中,有限差分方法的示例

以下是最终边界条件:

有限差分方法的示例有限差分方法的示例

  1. 用图案在时间上迭代解决方案,直到覆盖整个域。

以下是内部节点:

有限差分方法的示例有限差分方法的示例有限差分方法的示例有限差分方法的示例

我们可以将算法的数值结果排列如下的表格中,使用变换后的变量(上表)或原始变量(下表),在这里我们可以发现,对于S=75t=0,期权价格为£4.20

有限差分方法的示例

有限差分定价的示例。

总结

在本章中,我们回顾了今天用于定价金融衍生品的三种关键数值方法的基础知识。对于每一种方法,我们都提供了算法和数值示例。此外,这些方法的更高级特性可以在(Glasserman 2003)、(Kloeden and Platen 1992)和(Wilmott et al. 1995)等优秀教科书中找到,正如在前面讨论的所有部分中提到的那样。

并非所有方法都适用于所有情况,就像工具箱中的工具一样。有些方法更有效地解决一些特定的问题。例如,使用二项树,评估美式期权也很简单,而对于蒙特卡洛,就不那么直接了。蒙特卡洛在高维问题中更加强大,而有限差分可以有效地用于低维问题。

第四章. C++中的股票衍生品

在前两章中,我们描述了用于模拟金融衍生品基础资产行为的关键数学模型(第二章,数学模型)和用于定价的主要数值方法(第三章,数值方法)。

在本章中,我们将这些要素应用于股票衍生品的定价。我们考虑两个例子:普通的欧式看涨期权的定价(基本示例)和两种资产的最大值的股票篮子的定价(高级示例)。我们为两者提供完整的 C++实现。请注意,如果您对面向对象编程(OOP)不熟悉,建议您先学习 C 中的实现,然后再学习 C++中的实现,这些实现可以在本章的代码包中找到。

基本示例 - 欧式看涨期权

maineq1.cpp) is the pricing algorithm proper, while code snippets 2 and 3 are auxiliary functions. The algorithm is composed of six steps, which take us from the input parameters (STEP 1) to the output of the premium value (STEP 6).
random.cpp). This implements the Box-Muller method to obtain random samples from the standard normal (Gaussian) distribution that are required for the GBM. Code snippet 3 (random.h) is simply the header file of code snippet 2 (random.cpp). The Box-Muller method takes two independent samples from a uniform distribution and transforms them into a single sample from a Gaussian distribution; this value needs to be assigned to the variable epsilon in the code. Certainly, a more efficient implementation is possible. The Box-Muller method in fact transforms a couple of uniform variables into a couple of normal variables. It would be better to also use the second normal sample, generated in the process, in order to be computationally more effective. Please refer to the book website for details of this more efficient implementation and to the original paper for further details (*A Note on the Generation of Random Normal Deviates*).

作为输入参数的一部分,我们应该定义NM。这里N代表在 GBM 计算中使用的时间步数,M代表要使用的蒙特卡洛模拟次数。在我们的示例中,我们考虑了巴克莱股票(BARC.L)的欧式看涨期权的定价,其现货价为£100,行权价为£100,无风险利率为 5%每年,年化波动率为 10%,到期期限为一年。我们使用N=500M=10,000。在我的计算机上,期权费为£6.81,执行时间为 1.34 秒。期权费和执行时间将因计算机而异。

请注意,通过简单更改算法中的步骤 4,可以轻松修改此代码以定价其他支付。在 C++实现方面,可以使用类来定义支付。此外,步骤 4可以稍作修改,以包括蒙特卡洛逼近的准确度估计。请参阅网站以获取包含这些功能的可下载实现。描述此示例的优秀教材是金融期权定价导论:数学、随机过程和计算

代码 1 - EQ1 - 蒙特卡罗欧式看涨期权

以下是EQ1_main.cpp文件的代码片段:

// maineq1.cpp
// requires random.cpp
#include "random.h"
#include <iostream>
#include <cmath>
#include <algorithm>

using namespace std;
int main()
{
  cout << "\n *** START EQ1: Monte Carlo European Call *** \n";
  // STEP 1: INPUT PARAMETERS
  double T=1; // maturity
  double K=100; // strike
  double S0=100; // spot
  double sigma=0.10; // volatility
  double r=0.05; // interest rate
  int N=500; // number of steps
  int M=10000; // number of simulations
  double S[N+1];
  double sumpayoff=0;
  double premium=0;
  double dt = T / N;

  // STEP 2: MAIN SIMULATION LOOP
  for (int j=0; j < M; j++)
  {
    S[0]=S0; // initialize each path for simulation

    // STEP 3: TIME INTEGRATION LOOP
    for (int i=0; i < N; i++)
    {
      double epsilon = SampleBoxMuller();  // get Gaussian draw
      S[i+1] = S[i]*(1+r*dt+sigma*sqrt(dt)*epsilon);
    }

    // STEP 4: COMPUTE PAYOFF
    sumpayoff += max(S[N]-K,0.0); // compute and ad payoff 
  }

  // STEP 5: COMPUTE DISCOUNTED EXPECTED PAYOFF
  premium =  exp(-r*T)*(sumpayoff / M);

  // STEP 6: OUTPUT RESULTS
  cout <<"premium =  " << premium << "\n";
  cout << "\n *** END EQ1: Monte Carlo single asset *** \n";

  return 0;
}

代码 2 - random.cpp 文件

 random.cpp file:
// random.cpp
// Computing Gaussian deviates using Box-Muller method

#include "Random.h"
#include <cstdlib>
#include <cmath>
using namespace std;

double SampleBoxMuller()
{
  double result;
  double x;
  double y;

  double xysquare;
  do
  {
    x = 2.0*rand()/static_cast<double>(RAND_MAX)-1;
    y = 2.0*rand()/static_cast<double>(RAND_MAX)-1;
    xysquare = x*x + y*y;
  }
  while
  ( xysquare >= 1.0);
  result = x*sqrt(-2*log(xysquare)/xysquare);
  return result;
}

代码 3 - random.h 头文件

以下是random.h文件的代码:

// random.h
double SampleBoxMuller();

编译和运行代码后,您应该获得以下屏幕截图:

基本示例 - 欧式看涨期权

提示

下载示例代码

您可以从www.packtpub.com的帐户中下载您购买的所有 Packt 图书的示例代码文件。如果您在其他地方购买了本书,可以访问www.packtpub.com/support并注册,以便直接通过电子邮件接收文件。

高级示例 - 股票篮子

with Code 1: first, regarding the input parameters (STEP 1) and second regarding the calculation of the GBM (STEP 4). We now need to specify the parameters for both processes, including their spot prices and volatilities. As we need to compute two correlated stochastic processes, the two Gaussian samples that are required are now computed as follows:

高级示例 - 股票篮子

在上述方程中,高级示例 - 股票篮子是来自高斯分布的两个独立样本,而高级示例 - 股票篮子是包含相关性效应的两个相关样本,其中包含相关性高级示例 - 股票篮子。Epsilon_1 和 epsilon_2 仍然是正态变量,因为它们具有单位方差,并且它们的乘积的期望值高级示例 - 股票篮子等于高级示例 - 股票篮子

与之前一样,我们可以在步骤 5中轻松修改支付,并纳入其他更复杂的支付。

例如,考虑以下篮子期权的价格:

我们有两个资产巴克莱银行(BARC.L)和劳斯莱斯(RR.L)。我们想要定价一个期权,该期权在到期时支付这两个资产的价值中的最大值,即一年。巴克莱的当前现货价格为 120 英镑,劳斯莱斯的现货价格为 100 英镑。它们的年化波动率分别为 10%和 15%。我们选择在 300 个时间步长内离散化时间,并使用 1 万次模拟。在这些条件下,该期权的保费为 120.48 英镑,执行时间为 2.22 秒。

有关股票篮子衍生品的更多细节,欢迎查阅《保罗·威尔莫特量化金融,第二版》。

代码 4 - EQ2 - 蒙特卡洛股票篮子

以下是EQ2_main.cpp文件的代码片段:

// maineq2.cpp
// requires random.cpp
#include "random.h"
#include <iostream>
#include <cmath>
#include <algorithm>
using namespace std;

int main()
{
  cout << "\n *** START EQ2: Monte Carlo equity basket *** \n";
  // STEP 1: INPUT PARAMETERS
  double T=1; // maturity
  double r=0.05; // interest rate
  double S10=120; // spot equity 1
  double S20=100; // spot equity 2
  double sigma1=0.10; // volatility
  double sigma2=0.15; // volatility
  double rho=0.5; // correlation
  int N=300; // number of steps
  int M=10000; // number of simulations
  double S1[N+1];
  double S2[N+1];
  double sumpayoff=0;
  double premium=0;
  double dt = T / N;

  // STEP 2: MAIN SIMULATION LOOP
  for (int j=0; j < M; j++)
  {
    S1[0]=S10;
    S2[0]=S20;
    // STEP 3: TIME INTEGRATION LOOP
    for (int i=0; i < N; i++)
    {
      double epsilon1 = SampleBoxMuller();
      double epsilon2 = SampleBoxMuller();
      S1[i+1] = S1[i]*(1+r*dt+sigma1*sqrt(dt)*epsilon1);
      epsilon2 = epsilon1*rho+sqrt(1-rho*rho)*epsilon2;
      S2[i+1]=S2[i]*(1+r*dt+sigma2*sqrt(dt)*epsilon2);
    }
    // STEP 4: TIME INTEGRATION LOOP
    sumpayoff += max(S1[N],S2[N]);
  }
  // STEP 5: COMPUTE DISCOUNTED EXPECTED PAYOFF
 premium =  exp(-r*T)*(sumpayoff / M);

  // STEP 6: OUTPUT RESULTS
  cout <<"premium =  " << premium << "\n";
  cout << "\n *** END EQ2: Monte Carlo equity basket *** \n";
  return 0;
}

编译和运行代码后,您应该获得以下屏幕截图:

高级示例 - 股票篮子

摘要

我们已经解决了权益衍生品中的两个定价问题。我们已经看到了一个非常简单的例子(我们称之为基本)和一个更复杂的例子,其中包括一个股票篮子期权。对于每一个问题,我们都提供了完整的 C++实现。

我们现在将继续进行下一个资产类别,外汇衍生品,在那里我们也将解决两个问题,一个简单的问题和一个高级的问题,遵循下一章中的 Bento Box 模板方法。

第五章:C++外汇衍生品

我们现在转向货币或外汇衍生品的世界,以及如何使用 C++对它们进行定价。我们考虑两个例子:欧洲看涨期权的定价(基本示例)和敲出障碍看涨期权的定价(高级示例)。我们为两者提供完整的 C++实现。我们遵循在“外汇期权价值”中找到的外汇货币演变模型。本章的代码包中可以找到一个更简单的 C 实现(不包括 OO 特性)。如果您对面向对象编程(OOP)不熟悉,建议您先学习 C 中的实现,然后再学习 C++中的实现。

基本示例-欧洲外汇看涨期权(FX1)

在这个例子中,我们演示了外汇普通欧式看涨期权的定价。我们的目标是计算这种金融衍生品的保费。

合同的全部细节,包括数学模型的选择及其数值方法,都显示在以下欧洲看涨外汇期权(FX1)的弁当盒模板中。

基本示例-欧洲外汇看涨期权(FX1)

欧洲看涨外汇期权(FX1)的弁当盒模板

我们按顺时针方向完成弁当盒的内容,从左上角开始。以下是这样做的步骤:

  1. 衍生合同:我们首先填写合同的所有数据,特别是支付函数,我们的情况如下:基本示例-欧洲外汇看涨期权(FX1)

方程 1

  1. 数学模型:我们应该选择基础资产的数学模型,在货币的情况下是 Garman-Kohlhagen 模型。

  2. 数值方法:我们选择要使用的数值方法,在这种情况下,我们选择有限差分方法。

  3. 算法:我们构建算法,将这些计算组合成一系列计算步骤,这将作为我们在 C++中实现它的蓝图。

请注意,与蒙特卡洛模拟相比,有限差分算法不需要随机数生成器来操作。所有计算都是确定性的。

有限差分方法的一个重要特点是它们需要定义一个网格。这个网格本质上是偏微分方程PDE)将被近似的坐标集合。例如,在股票的情况下,例如,Black-Scholes PDE 是根据两个独立变量定义的:股票价格S和时间t。在货币的情况下,Garman-Kohlhagen PDE 是根据两个变量定义的:汇率X和时间t。因此,解决域实际上是由在Xt平面上的所有可能值对Xt可以取的范围定义的区域。例如,如果我们考虑的是一种欧洲看涨货币期权,行权价为 1.0 EUR/GBP,现货价格为 1.0 EUR/GBP,到期日为一年,我们的解决域 gamma 可以被定义为X在 0.5 到 1.5 之间的值范围,以及t在 0 到 1 之间的值范围。在数学上,这可以表示如下:

基本示例-欧洲外汇看涨期权(FX1)

方程 2

这个矩形域需要被划分或离散化。这意味着我们必须将它从连续域转换为离散域。通常在有限差分中,我们将其分成X轴上的N个等距步长和t轴上的M个等距步长。结果是一个类似网格的网格,因此得名。

请注意,我们提供了显式有限差分方法的实现,如第三章中所述,数值方法,使用变量转换。这是为了将原始 PDE 转换为等效但简化的无量纲 PDE,描述了热的扩散。这个无量纲版本的 PDE 更容易使用 FDM 解决。

由于这种转换,解决域不会变成两个新变量x基本示例-欧洲外汇看涨期权(FX1)。因此,PDE 是在以下方程定义的域中解决的:

基本示例-欧洲外汇看涨期权(FX1)

方程 3

我们考虑一个欧洲看涨期权的例子,行权价为 0.75 欧元/美元,现货价格为 0.75 欧元/美元。该期权到期日为六个月。我们将x轴分为N=5步,tau 轴分为M=6步。在这些条件下,保费为 4.36 欧元/美元。

即将出现的代码片段实现了 Bento Box 模板中的算法。

代码 9-FX1_main.cpp(有限差分 FX 欧洲看涨期权)

FX1_main.cpp file:
// FX1_main.cpp
// requires FX_source.cpp, FX_print.cpp

#include "FX.h"

using namespace std;

int main()
{
  cout << "\n *** START FX1: Finite Difference European Call *** \n\n";

  // STEP 1: INPUT PARAMETERS
  auto T = 0.5; // maturity
  auto K = 75.0; // strike
  auto S0 = 75.0; // spot
  auto sigma = 0.30; // volatility
  auto r = 0.05; // interest rate
  auto dx = 0.5; // space step
  auto dt = 0.1; // time step
  auto N = 5; // number of space steps
  auto M = 6; // number of time steps

  // Construct a FX_EQ1 object from the input parameters:

  FX fx_eq1(T, K, S0, sigma, r, dt, dx, N, M);

  // Ask the object to evaluate the FX data for European Call:

  auto result = fx_eq1.get_data_and_premium();

  // STEP 7: OUTPUT RESULTS

  cout << result;

  cout << "\n *** END FX1: Finite Difference European Call *** \n";

  return 0;
}

代码 10-FX1_source.cpp(有限差分 FX 欧洲看涨期权)

FX1_source.cpp file:
// FX1_source.cpp
#include "FX.h"
#include "matrix.h"
#include <algorithm>
using namespace std;

result_data FX::evaluate_data_and_premium() const
{
  double dtau, alpha, k;

  vector<double> t, tau, S, x;

  matrix<double> u, v;

  matrix_resize(u, N, M);

  matrix_resize(v, N, M);

  // Therefore, both the matrices u, v are resized to N by M
  // Now, let us resize the vectors t, tau, S and x:
  t.resize(M);
  tau.resize(M);
  S.resize(N);
  x.resize(N);

  dtau = dt * (0.5*sigma*sigma);
  alpha = dtau / (dx*dx);
  k = r / (0.5*sigma*sigma);
  double xmin = -1;
  double xmax = +1;

  // STEP 2: SETUP MESH (x and tau grids)
  for (int i = 0; i < N; i++)
  {
    x[i] = xmin + i*dx;
    S[i] = K*exp(x[i]);
  }

  for (int j = 0; j < M; j++)
  {
    t[j] = j*dt;
    tau[j] = (T - t[j]) / (0.5*sigma*sigma);
  }

  // STEP 3: SETUP INITIAL CONDITION
  for (int i = 0; i < N; i++)
  {
    u[i][0] = max(exp(0.5*(k + 1)*x[i]) - exp(0.5*(k - 1)*x[i]), 0.0);
  }

  // STEP 4: SETUP BOUNDARY CONDITIONS
  for (int j = 1; j < M; j++)
  {
    u[0][j] = 0.0;
    u[N - 1][j] = u[N - 1][0];
  }

  // STEP 5: COMPUTE FORWARD DIFFERENCES
  for (int j = 0; j < M - 1; j++)
  {
    for (int i = 1; i < N - 1; i++)
    {
      u[i][j + 1] = alpha*u[i + 1][j] + (1 - 2 * alpha)*u[i][j] + alpha*u[i - 1][j];
    }
  }

  // STEP 6: TRANSFORM SOLUTION FROM X TO S COORDINATES (u and v)
  for (int j = 0; j < M; j++)
  {
    for (int i = 0; i < N; i++)
    {
      v[i][j] = pow(K, (0.5*(1 + k)))*pow(S[i], (0.5*(1 - k)))*exp(1.0 / 8.0*(k + 1)*(k + 1)*sigma*sigma*(T - t[i]))*u[i][j];
    }
  }

  result_data result(alpha, dtau, k, x, S, t, tau, u, v);

  return result;

}

提示

FX.h, code snippet 12 FX_print.cpp, and code snippet 13 matrix.h, please refer to the code bundle of the book.

要计算基本示例(FX1),您需要编译和运行代码片段 9、10、11、12 和 13(其中包括矩阵和打印实用程序);之后,您应该获得以下屏幕截图:

基本示例-欧洲外汇看涨期权(FX1)

基本示例(FX1):FX 欧洲看涨期权截图及结果

高级示例-FX 障碍期权(FX2)

在这个第二个示例中,我们考虑了一种奇异期权的定价:具有看涨回报的向上和向外障碍。方法的细节显示在以下 Bento Box 模板中,用于 FX 障碍向上和向外期权(FX2):

高级示例-FX 障碍期权(FX2)

FX 障碍向上和向外期权(FX2)的 Bento Box 模板

请注意,在定价连续监控的障碍期权方面,使用有限差分方法FDM)相对于蒙特卡洛MC)有很大的优势。这是因为 MC 相对复杂,难以纳入连续监控特性,只能增加 MC 程序中的固定/观察点的数量。然而,这将显著增加 MC 的计算时间。在 FDM 中我们不需要这样做,使其更有效率。

我们的目标是像之前一样计算期权保费。

向上和向外障碍就像标准的欧洲看涨期权,但有一个关键的区别-如果标的物越过限制的上限障碍,期权的价值就为零。因此,定价算法及其实施几乎相同,但不同之处在于上边界条件现在将值设为零。

障碍期权在金融中很有用,因为它们的保费比标准欧洲期权要低。它们对投资者来说更便宜,因为如果标的物的水平太高(向上和向外障碍)或太低(向下和向外障碍),投资者就要承担不行使期权的风险。

我们考虑与之前相同的例子,但障碍为B=1.5欧元/美元。在这些条件下,该期权的保费为 4.11 欧元/美元,执行时间为 2.22 秒。

即将出现的代码片段实现了 Bento Box 模板中的算法。

代码 14-FX2_main.cpp(FDM FX 障碍期权)

FX2_main.cpp file:
// FX2_main.cpp
// requires FX2_source.cpp, FX_print.cpp

#include "FX.h"
#include <iostream>

using namespace std;

int main()
{
  cout << "\n *** START FX2: Finite Difference"
    << " European Up-and-Out Barrier Call *** \n\n";

  // STEP 1: INPUT PARAMETERS
  auto T = 0.5; // maturity
  auto K = 75.0; // strike
  auto S0 = 75.0; // spot
  auto sigma = 0.30; // volatility
  auto r = 0.05; // interest rate
  auto dx = 0.5; // space step
  auto dt = 0.1; // time step
  auto N = 5; // number of space steps
  auto M = 6; // number of time steps

  // Construct a FX object from the input parameters:

  FX fx_eq2(T, K, S0, sigma, r, dt, dx, N, M);

  // Ask the object to evaluate the FX data
  // for European Up-and_Out Barrier Call:

  auto result = fx_eq2.get_data_and_premium();

  // STEP 7: OUTPUT RESULTS
  cout << result;

  cout << "\n *** END FX2: Finite Difference"
    << " European Up-and-Out Barrier Call *** \n";

  return 0;
}

代码 15-FX2_source.cpp(FDM FX 障碍期权)

FX2_source.cpp file:
// FX2_source.cpp

#include "FX.h"
#include "matrix.h"
#include <algorithm>

using namespace std;

result_data FX::evaluate_data_and_premium() const
{
  double dtau, alpha, k
  vector<double> t, tau, S, x
  matrix<double> u, v
  auto resz = this {

    // to make number of rows = 
    u.resize(N);

    // to make number of columns = 
    for (auto& row : u)
    row.resize(M);
  };

  resz(u, N, M)
  resz(v, N, M);

  // Therefore, both the matrices u, v are resized to N by M
  // Now, let us resize the vectors t, tau, S and x:
  t.resize(M);
  tau.resize(M);
  S.resize(N);
  x.resize(N);

  dtau = dt * (0.5*sigma*sigma);
  alpha = dtau / (dx*dx);
  k = r / (0.5*sigma*sigma);

  double xmin = -1;
  double xmax = +1;

  // STEP 2: SETUP MESH (x and tau grids)
  for (int i = 0; i < N; i++)
  {
    x[i] = xmin + i*dx;
    S[i] = K*exp(x[i]);
  }

  for (int j = 0; j < M; j++)
  {
    t[j] = j*dt;
    tau[j] = (T - t[j]) / (0.5*sigma*sigma);
  }

  // STEP 3: SETUP INITIAL CONDITION
  for (int i = 0; i < N; i++)
  {
    u[i][0] = max(exp(0.5*(k + 1)*x[i]) - exp(0.5*(k - 1)*x[i]), 0.0);
  }

  // STEP 4: SETUP BOUNDARY CONDITIONS
  for (int j = 1; j < M; j++)
  {
    u[0][j] = 0.0;
    u[N - 1][j] = 0.0;
  }

  // STEP 5: COMPUTE FORWARD DIFFERENCES
  for (int j = 0; j < M - 1; j++)
  {
    for (int i = 1; i < N - 1; i++)
    {
      u[i][j + 1] = alpha*u[i + 1][j] + (1 - 2 * alpha)*u[i][j] + alpha*u[i - 1][j];
    }
  }

  // STEP 6: TRANSFORM SOLUTION FROM X TO S COORDINATES (u and v)
  for (int j = 0; j < M; j++)
  {
    for (int i = 0; i < N; i++)
    {
      v[i][j] = pow(K, (0.5*(1 + k)))*pow(S[i], (0.5*(1 - k)))
      *exp(1.0 / 8.0*(k + 1)*(k + 1)*sigma*sigma*(T - t[i]))*u[i][j];
    }
  }

  result_data result(alpha, dtau, k, x, S, t, tau, u, v);

  return result;

}

计算高级示例(FX2)需要编译和运行代码片段 14 和 15 以及之前的 11、12 和 13;之后,您应该获得以下屏幕截图:

高级示例-FX 障碍期权(FX2)

高级示例(FX2):FX 向上和向外障碍看涨期权截图及结果

总结

在本章中,我们已经解决了外汇衍生品中的两个定价问题。我们已经看到了一个基本的例子和一个更复杂的例子(普通香草),以及一个高级的例子(异国情调),包括一个障碍期权。对于每一个,我们都提供了完整的 C++实现。

我们现在将继续进行下一个资产类别和利率衍生品。

第六章:C++利率衍生品

本章说明了 C++在利率衍生品定价中的应用。我们将考虑两个示例:普通香草利率互换IRS)的定价(基本示例)和 Cap 的定价(高级示例)。我们为两者提供了完整的 C++实现。这两个示例都使用了一因子Libor 市场模型LMM)和蒙特卡洛模拟。伴随书籍网站上可以找到一个更简单的 C 实现(没有面向对象的特性)。LMM 在“利率动态的市场模型”中有描述。蒙特卡洛模拟的优秀描述可以在“有效方法评估利率衍生品”中找到。

基本示例-普通香草 IRS(IR1)

STEP 1) to the output of the present value of the IRS (STEP 10).

请注意,蒙特卡洛模拟需要一个随机数生成器来运行。我们将利用我们在第三章中开发的随机数生成器,来定价权益衍生品(Box-Muller 算法)。

我们考虑一个名义为一百万欧元的普通香草 IRS 的例子。合同期限为一年,支付频率为每三个月一次。因此,浮动利率是以 EURIBOR3M 为基准的。固定利率为 4%。

我们使用带有 1 万次模拟的 Monte Carlo 模拟的 LMM。我们假设初始的利率期限结构是 5%。我们还假设远期利率的波动率为 15%(这个值通常是根据市场上观察到的掉期进行校准的)。

即将出现的代码片段实现了 Bento Box 模板中的算法。

代码 16-IR1_main.cpp(带 Monte Carlo LMM 的 IRS)

 for IR1_main.cpp file:
// IR1_main.cpp
// requires random.cpp IR1_source.cpp

#include "IR.h"
#include <iostream>
using namespace std;

int main()
{

  cout << "\n *** START IR1: IRS Monte Carlo Libor Market Model 1F * ** \n\n";

  // Plain Vanilla IRS, pays fixed, receives floating
  // freq payments every 3M, maturity 1 year

  // STEP 1: INPUT PARAMETERS
  double notional = 1000000; // notional
  double K = 0.04; // fixed rate IRS
  double alpha = 0.25; // daycount factor
  double sigma = 0.15; // fwd rates volatility
  double dT = 0.25;
  int N = 3; // number forward rates
  int M = 1000; // number of simulations

  // Construct a IR object from the input parameters:

  IR ir1(notional, K, alpha, sigma, dT, N, M);

  // Obtain the value of premium from member function "get_premium()":

  auto results = ir1.get_simulation_data();

  // STEP 10: OUTPUT RESULTS
  auto sz = results.datapoints.size();
  for (decltype(sz) nsim = 0; nsim < sz; ++nsim)
  {
    cout << "simIRS[" << nsim << "] = " << results.datapoints[nsim] << endl;
  }

  cout << "\n *** IRS PV = " << results.Value << endl;
  cout << "\n *** END IR1: IRS Monte Carlo Libor Market Model 1F *** \n";

  return 0;
}

代码 17-IR1_source.cpp(带 Monte Carlo LMM 的 IRS)

以下是IR1_source.cpp文件的代码片段:

// IR1_source.cpp

#include "IR.h"
#include "random.h"
#include "matrix.h"
#include <algorithm>
#include <iostream>

using namespace std;

IR_results IR::run_LIBOR_simulations() const
{
  matrix<double> L; // forward rates
  matrix_resize(L, N + 1, N + 1);
  matrix<double> D; // discount factors
  matrix_resize(D, N + 2, N + 2);
  vector<double> dW(N + 1); // discount factors
  vector<double> FV(N + 2); // future value payment
  vector<double> FVprime(N + 2); // numeraire-rebased FV payment
  vector<double> V(M); // simulation payoff

  // Composing the SampleBoxMuller class:

  SampleBoxMuller normal;

  double df_prod = 1.0;
  double drift_sum = 0.0;
  double sumPV = 0.0;
  double PV = 0.0;

  // STEP 2: INITIALISE SPOT RATES
  L[0][0] = 0.05;
  L[1][0] = 0.05;
  L[2][0] = 0.05;
  L[3][0] = 0.05;

  // start main MC loop

  for (int nsim = 0; nsim < M; ++nsim)
  {

    // STEP 3: BROWNIAN MOTION INCREMENTS
    dW[1] = sqrt(dT)*normal();
    dW[2] = sqrt(dT)*normal();
    dW[3] = sqrt(dT)*normal();

    // STEP 4: COMPUTE FORWARD RATES TABLEAU
    for (int n = 0; n < N; ++n)
    {
      for (int i = n + 1; i < N + 1; ++i)
      {
        drift_sum = 0.0;
        for (int k = i + 1; k < N + 1; ++k)
        {
          drift_sum += (alpha*sigma*L[k][n]) / (1 + alpha*L[k][n]);
        }
        L[i][n + 1] = L[i][n] * exp((-drift_sum*sigma - 0.5*sigma*sigma)*dT + sigma*dW[n + 1]); // cout <<"L: i= " << i <<", n+1 = " << n+1 " << L[i][n+1] << "\n";
      }
    }
    // STEP 5: COMPUTE DISCOUNT RATES TABLEAU
    for (int n = 0; n < N + 1; ++n)
    {
      for (int i = n + 1; i < N + 2; ++i)
      {
        df_prod = 1.0;
        for (int k = n; k < i; k++)
        {
          df_prod *= 1 / (1 + alpha*L[k][n]);
        }
        D[i][n] = df_prod;
        // cout <<"D: i = " << i <<", n = " << n <<", D[i][n] = " << D[i][n] << "\n";
      }
    }

    // STEP 6: COMPUTE EFFECTIVE FV PAYMENTS
    FV[1] = notional*alpha*(L[0][0] - K);
    FV[2] = notional*alpha*(L[1][1] - K);
    FV[3] = notional*alpha*(L[2][2] - K);
    FV[4] = notional*alpha*(L[3][3] - K);

    // STEP 7: COMPUTE NUMERAIRE-REBASED PAYMENT
    FVprime[1] = FV[1] * D[1][0] / D[4][0];
    FVprime[2] = FV[2] * D[2][1] / D[4][1];
    FVprime[3] = FV[3] * D[3][2] / D[4][2];
    FVprime[4] = FV[4] * D[4][3] / D[4][3];

    // STEP 8: COMPUTE IRS NPV

    V[nsim] = FVprime[1] * D[1][0] + FVprime[2] * D[2][0] + FVprime[3] * D[3][0] + FVprime[4] * D[4][0];
  }
  // end main MC loop

  // STEP 9: COMPUTE DISCOUNTED EXPECTED PAYOFF
  sumPV = 0.0;
  for (int nsim = 0; nsim < M; nsim++)
  {
    sumPV += V[nsim];
  }

  PV = sumPV / M;

  IR_results results(V, PV);

  return results;
}

提示

IR.h, please refer to the code in the code bundle.

要计算基本示例(IR1),您将需要编译和运行代码片段 16、17、18、4、5 和 13(包括头文件、矩阵和随机函数)。之后,您应该获得以下屏幕:

基本示例-普通香草 IRS(IR1)

基本示例(IR1)屏幕截图与结果

高级示例-带上限的 IRS(IR2)

在第二个示例中,我们考虑了带有上限的 IRS 的定价。该方法的详细信息显示在以下 Bento Box 模板的高级示例中:

高级示例-带上限的 IRS(IR2)

高级示例(IR2)的 Bento Box 模板

我们的目标是计算互换的现值,就像我们之前做的那样。

IRS 上限与标准 IRS 非常相似,但有一个关键区别-在每个付款日期,我们计算浮动利率和行权价之间的最大值和零之间的差值。有了这个差值,我们计算一个上限单利的价值;上限就是 IRS 中包含的上限单利的总和。

算法显示在 Bento Box 模板的框 4 中。C++中算法的实现显示在代码片段 19 和 20 中。代码片段 19 是主要代码块,而代码片段 20 是其相关源代码。

代码 19-IR2_main.cpp(带 Monte Carlo LMM 的上限)

 IR2_main.cpp file:
// IR2_main.cpp
// requires random.cpp IR2_source.cpp

#include "IR.h"
#include <iostream>
using namespace std;

int main()
{
  std::cout << "\n *** START IR2: CAP Monte Carlo Libor Market Model 1F * ** \n\n";

  // STEP 1: INPUT PARAMETERS
  double K = 0.05; // strike caplet
  double alpha = 0.5; // daycount factor
  double sigma = 0.15; // fwd rates volatility
  double dT = 0.5;
  int N = 4; // number forward rates
  int M = 1000; // number of simulations

  // Construct a IR object from the input parameters:
  IR ir2(K, alpha, sigma, dT,  N, M);

  // Obtain the value of premium from member function "get_premium()":

  auto results = ir2.get_simulation_data();

  // STEP 10: OUTPUT RESULTS
  auto sz = results.datapoints.size();

  for (decltype(sz) nsim = 0; nsim < sz; ++nsim)
  {
    cout << "Vcap[" << nsim << "] = " << results.datapoints[nsim] << endl;
  }

  cout << "\n *** IRS cap = " << results.Value << "\n";

  cout << "\n *** END IR2: CAP Monte Carlo Libor Market Model 1F * ** \n";

  return 0;
}

代码 20-IR2_source.cpp(带 Monte Carlo LMM 的上限)

for IR2_source.cpp file:
// IR2_source.cpp

#include "IR.h"
#include "random.h"
#include "matrix.h"
#include <algorithm>
#include <iostream>

using namespace std;

IR_results IR::run_LIBOR_simulations() const
{
  matrix<double> L; // forward rates
  matrix_resize(L, N + 1, N + 1);
  matrix<double> D; // discount factors
  matrix_resize(D, N + 2, N + 2);
  vector<double> dW(N + 1); // discount factors
  vector<double> V(N + 2); // caplet payoff
  vector<double> Vprime(N + 2); // numeraire-rebased caplet payoff
  vector<double> Vcap(M); // simulation payoff

  // Composing the SampleBoxMuller class:
  SampleBoxMuller normal;

  double df_prod = 1.0;
  double drift_sum = 0.0;
  double sumcap = 0.0;
  double payoff = 0.0;

  // STEP 2: INITIALISE SPOT RATES
  L[0][0] = 0.05;
  L[1][0] = 0.05;
  L[2][0] = 0.05;
  L[3][0] = 0.05;
  L[4][0] = 0.05;

  // start main MC loop

  for (int nsim = 0; nsim < M; ++nsim)
  {
    // STEP 3: BROWNIAN MOTION INCREMENTS
    dW[1] = sqrt(dT)*(normal());
    dW[2] = sqrt(dT)*(normal());
    dW[3] = sqrt(dT)*(normal());
    dW[4] = sqrt(dT)*(normal());

    // STEP 4: COMPUTE FORWARD RATES TABLEAU
    for (int n = 0; n < N; ++n)
    {
      for (int i = n + 1; i < N + 1; ++i)
      {
        drift_sum = 0.0;
        for (int k = i + 1; k < N + 1; ++k)
        {
          drift_sum += (alpha*sigma*L[k][n]) / (1 + alpha*L[k][n]);
        }
        L[i][n + 1] = L[i][n] * exp((-drift_sum*sigma - 0.5*sigma*sigma)*dT 
        + sigma*dW[n + 1]);
        // cout <<"L: i = " << i <<", n+1 = " << n+1 <<", = " << L[i][n+1] << "\n";
      }
    }

    // STEP 5: COMPUTE DISCOUNT RATES TABLEAU
    for (int n = 0; n < N + 1; ++n)
    {
      for (int i = n + 1; i < N + 2; ++i)
      {
        df_prod = 1.0;
        for (int k = n; k < i; k++)
        {
          df_prod *= 1 / (1 + alpha*L[k][n]);
        }
        D[i][n] = df_prod;
        // cout <<"D: i = " << i <<", n = " << n <<", D[i][n] = " 
        //		<< D[i][n] << "\n";
      }
    }

    // STEP 6: COMPUTE CAPLETS
    double diff;
    diff = L[0][0] - K;
    V[1] = max(diff, 0.0);
    diff = L[1][1] - K;
    V[2] = max(diff, 0.0);
    diff = L[2][2] - K;
    V[3] = max(diff, 0.0);
    diff = L[3][3] - K;
    V[4] = max(diff, 0.0);
    diff = L[4][4] - K;
    V[5] = max(diff, 0.0);

    // STEP 7: COMPUTE NUMERAIRE-REBASED CAPLETS
    Vprime[1] = V[1] * D[1][0] / D[5][0];
    Vprime[2] = V[2] * D[2][1] / D[5][1];
    Vprime[3] = V[3] * D[3][2] / D[5][2];
    Vprime[4] = V[4] * D[4][3] / D[5][3];
    Vprime[5] = V[5] * D[5][4] / D[5][4];

    // STEP 8: COMPUTE CAP PAYOFF
    Vcap[nsim] = Vprime[1] + Vprime[2] + Vprime[3] + Vprime[4] + Vprime[5];
  }
  // end main MC loop

  // STEP 9: COMPUTE DISCOUNTED EXPECTED PAYOFF
  sumcap = 0.0;

  for (int nsim = 0; nsim < M; ++nsim)
  {
    sumcap += Vcap[nsim];
  }

  payoff = D[N + 1][0] * sumcap / M;

  IR_results results(Vcap, payoff);

  return results;
}

我们考虑了一个带有 5%行权价和 2.5 年到期的 IRS 的例子。我们假设 5%的固定利率期限结构和 15%的远期波动率。支付是每六个月一次,名义金额为 1 欧元。浮动利率是 EURIBOR6M。

要计算高级示例(IR2),您需要编译和运行代码片段 19、20、18、4、5 和 13(包括一个头文件、矩阵和随机函数)。之后,您应该获得以下屏幕截图:

高级示例-带上限的 IRS(IR2)

高级示例(IR2)屏幕截图与结果

总结

在本章中,我们已经解决了利率衍生品中的两个定价问题。我们已经看到了一个基本示例(普通香草利率互换)和一个高级示例。对于每一个,我们都提供了完整的 C++实现。

我们现在将在下一章继续讨论最后一个资产类别,信用衍生品。

第七章:C++中的信用衍生品

在这最后一章中,我们专注于将 C++应用于信用衍生品的定价。我们考虑了两个例子:使用 Merton 模型定价可违约公司的股票加上公司的违约概率(基本示例)以及信用违约互换CDS)的定价(高级示例)。第一个例子基于信用风险的结构方法,而第二个例子基于强度方法。我们为这两个示例提供了完整的 C++实现。伴随书籍网站上可以找到一个更简单的 C 实现(不包括 OO 特性)。

基本示例 - 破产(CR1)

STEP 1) to the output of the premium value (STEP 6).

蒙特卡洛模拟需要随机数生成器来运行,因此,random.cpp文件(在第四章中学习,C++中的股票衍生品)被重复使用。

我们考虑一个公司的资本结构的例子,该结构由t=0时的总公司资产V(0)=100百万欧元和一张面值为D=70百万欧元的单一零息债券组成。假定公司资产的波动率为 20%。到期日为四年。无风险利率为 5%。

运行 C++代码片段如下图所示,使用 500 步和 10,000 次模拟,我们估计四年期间违约的概率为 88.63%,股权价值为E(0)=43.95百万欧元:

![基本示例 - 破产(CR1)](img/00209.jpeg)

公司破产的便当盒模板(CR1)

即将出现的代码片段实现了便当盒模板中的算法。

代码 21 - CR1_main.cpp(使用 Merton 模型的破产)

 for CR1_main.cpp file:
// CR2_main.cpp

// It requires CR2_source.cpp
#include "CR2.h"

#include <iostream>

using namespace std;

int main()
{
  cout << "\n *** START CR2: Credit Default Swap *** \n";

  // STEP 1: INPUT PARAMETERS

  auto T = 1.0; // maturity
  auto N = 4; // number of payments per year
  auto notional = 100.0; // notional
  auto r = 0.05; // risk free interest rate
  auto h = 0.01; // hazard rate
  auto rr = 0.50; // recovery rate

  // Construct a CR2 object from the input parameters:

  CR2 cr2(T, N, notional, r, h, rr);

  // Obtain the value of premium from member function "get_premium()":

  auto cr2_results = cr2.get_pv_premium_and_default_legs_and_cds_spread();

  // STEP 6: OUTPUT RESULTS

  cout << "\n PV premium leg =  "
    << cr2_results.pv_premium_leg << "\n";

  cout << "\n PV default leg =  "
    << cr2_results.pv_default_leg << " \n";

  cout << "\n cds_spread =  "
    << cr2_results.cds_spread_in_bps << "  bps \n";

  cout << "\n *** END CR2: Credit Default Swap *** \n";

  return 0;
}

代码 22 - CR1_source.cpp(使用 Merton 模型的破产)

 CR1_source.cpp file:
// CR2_source.cpp

#include "CR2.H"
#include <vector>
#include <cmath>

using namespace std;

CR2_results CR2::find_pv_premium_and_default_legs_and_cds_spread() const
{
  auto pv_premium_leg = 0.0; // sum premium leg
  auto pv_default_leg = 0.0; // sum default leg
  auto t = 0.0; // current time
  auto cds_spread = 0.0;
  auto array_size = static_cast<int>(N*T + 1);
  vector <double> DF(array_size);
  vector <double> P(array_size);
  P[0] = 1.0;
  auto dt = T / N;

  // STEP 2: LOOP FOR ALL PAYMENTS
  for (int j = 1; j < array_size; j++)
  {
    t = j*dt;
    DF[j] = exp(-r*t);
    P[j] = exp(-h*t);

    // STEP 3: COMPUTE PREMIUM PAYMENTS
    pv_premium_leg = pv_premium_leg + DF[j] * notional*dt*P[j];

    // STEP 4: COMPUTE DEFAULT PAYMENTS
    pv_default_leg = pv_default_leg + DF[j] * (1.0 - rr)*notional*(P[j - 1] - P[j]);
  }

  // STEP 5: COMPUTE CDS SPREAD
  cds_spread = pv_default_leg / pv_premium_leg;

  // Composing the CR2_results class:
  CR2_results results;
  results.pv_premium_leg = pv_premium_leg;
  results.pv_default_leg = pv_default_leg;
  results.cds_spread_in_bps = cds_spread * 10000;
  return results;
}

提示

CR1.h, please refer to the code in the code bundle.

要计算基本示例(CR1),您需要编译和运行代码片段 21、22、23、4 和 5(其中包括头文件和随机函数);之后,您应该获得以下屏幕截图:

![基本示例 - 破产(CR1)](img/00210.jpeg)

公司破产(CR1)屏幕截图与结果

高级示例 - 信用违约互换(CDS)(CR2)

在这个第二个例子中,我们考虑 CDS 的定价。该方法的细节显示在以下 CDS 的便当盒模板中:

![高级示例 - 信用违约互换(CDS)(CR2)](img/00211.jpeg)

CDS 的便当盒模板(CR2)

CDS 是两个交易对手 A 和 B 之间的金融合同,其中一方支付给另一方以购买对基础资产可能违约的信用保护。

在结构上,CDS 类似于普通的利率互换,因为它由各方之间的现金流交换组成。在典型的五年期 CDS 中,对手方 A 向 B 支付一系列定期的保险费支付,按照约定的名义金额。只要基础资产 C“存活”(即不违约),这些支付将会进行。

对手方 B 在基础资产 C 违约时向 A 支付单一的有条件支付。支付的金额等于名义金额减去恢复率。在数学术语中,它可以表示如下:

![高级示例 - 信用违约互换(CDS)(CR2)](img/00212.jpeg)

就像在 IRS 中一样,合同的“价格”是通过计算每条腿的现值(预期的保险费支付之和称为保险费腿PL)和预期的违约支付之和称为违约腿DL))来获得的。在数学术语中,PL 和 DL 表示如下:

![高级示例 - 信用违约互换(CDS)(CR2)](img/00213.jpeg)![高级示例 - 信用违约互换(CDS)(CR2)](img/00214.jpeg)

在前述方程中,P(T)是时间t的生存概率,N是名义金额,R是违约率,DF(t)是时间t的折现因子。为了公平定价,这些腿必须相等,通过这样,我们可以确定应支付的保险费的公平价值(也称为 CDS 利差)。这种利差的价值,用希腊字母高级示例 - CDS(CR2)表示,被视为 CDS 合同的价格。在数学术语中,它可以表示如下:

高级示例 - CDS(CR2)

我们在 CDS 的便当盒模板中提出的定价算法试图从前述方程中计算保险费。

正如我们所看到的,这种计算是确定性的,因此不需要蒙特卡洛模拟。我们在这里阐述的信用模型是基于“定价面临信用风险的金融证券衍生品”的“强度模型”的一个例子。

We will consider the example where the contract duration is one year, quarterly payments (that is, four payments per year), notional = 100 million USD, risk-free rate = 5 percent pa, hazard rate of underlying = 1 percent pa, recovery rate = 50 percent. For these inputs, the CDS spread is 50.0626 basis points.

在下图中,我们发现便当盒框架应用于我们的 CDS 问题:

高级示例 - CDS(CR2)

CDS(CR2)的便当盒模板

接下来的代码片段实现了便当盒模板中的算法。

代码 24 - CR2_main.cpp(CDS)

for CR2_main.cpp file:
// CR2_main.cpp

// It requires CR2_source.cpp
#include "CR2.h"
#include <iostream>

using namespace std;

int main()
{
  cout << "\n *** START CR2: Credit Default Swap *** \n";

  // STEP 1: INPUT PARAMETERS
  auto T = 1.0; // maturity
  auto N = 4; // number of payments per year
  auto notional = 100.0; // notional
  auto r = 0.05; // risk free interest rate
  auto h = 0.01; // hazard rate
  auto rr = 0.50; // recovery rate

  // Construct a CR2 object from the input parameters:
  CR2 cr2(T, N, notional, r, h, rr);

  // Obtain the value of premium from member function "get_premium()":

  auto cr2_results = cr2.get_pv_premium_and_default_legs_and_cds_spread();

  // STEP 6: OUTPUT RESULTS
  cout << "\n PV premium leg =  "
    << cr2_results.pv_premium_leg << "\n";

  cout << "\n PV default leg =  "
    << cr2_results.pv_default_leg << " \n";

  cout << "\n cds_spread =  "
    << cr2_results.cds_spread_in_bps << "  bps \n";

  cout << "\n *** END CR2: Credit Default Swap *** \n";

  return 0;
}

代码 25 - CR2_source.cpp(CDS)

以下是CR2_source.cpp文件的代码片段:

// CR2_source.cpp

#include "CR2.H"
#include <vector>
#include <cmath>

using namespace std;

CR2_results CR2::find_pv_premium_and_default_legs_and_cds_spread() const
{
  auto pv_premium_leg = 0.0; // sum premium leg
  auto pv_default_leg = 0.0; // sum default leg
  auto t = 0.0; // current time
  auto cds_spread = 0.0;
  auto array_size = static_cast<int>(N*T + 1);
  vector <double> DF(array_size);
  vector <double> P(array_size);

  P[0] = 1.0;

  auto dt = T / N;

  // STEP 2: LOOP FOR ALL PAYMENTS
  for (int j = 1; j < array_size; j++)
  {
    t = j*dt;
    DF[j] = exp(-r*t);
    P[j] = exp(-h*t);

    // STEP 3: COMPUTE PREMIUM PAYMENTS
    pv_premium_leg = pv_premium_leg + DF[j] * notional*dt*P[j];

    // STEP 4: COMPUTE DEFAULT PAYMENTS
    pv_default_leg = pv_default_leg + DF[j] * (1.0 - rr)*notional*(P[j - 1] - P[j]);
  }

  // STEP 5: COMPUTE CDS SPREAD
  cds_spread = pv_default_leg / pv_premium_leg;

  // Composing the CR2_results class:
  CR2_results results;
  results.pv_premium_leg = pv_premium_leg;
  results.pv_default_leg = pv_default_leg;
  results.cds_spread_in_bps = cds_spread * 10000;
  return results;
}

提示

CR2.h, please refer to the code in the code bundle.

计算高级示例(CR2)时,您将需要编译和运行代码片段 24、25 和 26;之后,您应该会得到以下截图:

高级示例 - CDS(CR2)

CDS(CR2)截图和结果

摘要

在本章中,我们已经解决了信用衍生品中的两个定价问题。我们看到了一个基本示例(使用结构模型)和一个更高级的示例(使用强度模型)。还有许多可能的变体和更复杂的合同,但这两个是将给您一个在这个迷人的资产类别中如何前进的想法的主要家族。这结束了我们对在 C++中实现不同类型的金融衍生品的示例的调查。

附录 A:期权定价的 C++数值库

在 C++中实现金融衍生品可能是一个复杂的任务。正如我们在本书中所展示的,它不仅需要对数学模型和数值方法的知识,以便在 C++代码的形式中实现它们,还需要使用可靠的数学和金融库的支持。例如,当您需要从标准正态分布中获取随机样本,或者当您需要求逆矩阵时。在这些情况下,我们可以使用已经存在的数值库,而不是从头开始实现这些算法。这些库包含多年来使用的算法,因此在许多用户之前已经得到验证。使用这些库将显著加速我们对高级定价模型的实现。这些库的一些示例将在接下来的章节中提到。

数值配方

许可证:商业。

网站:www.nr.com

在书籍“Numerical Recipes: The Art of Scientific Computing, 3rd Edition”中可以找到一套广泛使用和可靠的 C++数值例程。这套例程被世界各地顶尖大学和研究机构视为“黄金标准”。该书包含了这些例程的理论背景描述,并提供了 C++代码的访问。书中包含了 400 多个 C++数值例程,涵盖了线性代数方程的解、矩阵代数、插值和外推、积分和随机数等主题。

金融数值配方

许可证:免费/GNU。

网站:finance.bi.no/~bernt/gcc_prog/

这个网站包含了由 Bernt Arne Odegaard 开发的大量非常有用的 C++数值和金融程序。它们遵循 ANSI C++标准,并有一个名为Circa(250 页)的大型附带手册,其中包含使用的公式和相关参考资料。这个库可以在finance.bi.no/~bernt/gcc_prog/找到。

QuantLib 项目

许可证:免费/GNU。

网站:quantlib.org/

QuantLib 项目是一个为量化金融提供软件的大型项目。它已被用于金融领域的建模、交易和风险管理。该软件是用 C++编写的,并已导出到各种语言,如 C#、Objective Caml、Java、Perl、Python、GNU R、Ruby 和 Scheme。QuantLib 有许多有用的工具,包括收益曲线模型、求解器、PDEs、蒙特卡洛(低差异性)、异国期权、VAR 等。

Boost 库

许可证:免费/GNU。

网站:www.boost.org

Boost 项目提供了经过同行评审的便携式 C++源代码库,可以在 GNU GPL 下免费使用。这些库旨在使它们在广泛的应用程序中有用和可用。十个 Boost 库包括在 C++标准委员会的库技术报告(TR1)和新的 C++11 标准中。示例包括累加器、数组、时间、文件系统、几何、数学、数学/统计分布和 MPI。

GSL 库

许可证:免费/GNU。

网站:www.gnu.org/s/gsl/

GNU 科学图书馆(GSL)是用于 C 和 C ++的数值库。该库提供了各种数学数值例程,包括随机数生成器、特殊函数和最小二乘拟合。总共有 1000 多个函数。该库涵盖的主题领域的示例包括复数、多项式的根、特殊函数、向量和矩阵、排列、线性代数、特征系统、快速傅立叶变换、积分、随机数、准随机序列、统计、直方图和蒙特卡洛积分。

附录 B:参考文献

第二章

  • 加曼,M.B.,和 S.W Kohlhagen。“外币期权价值”。《国际货币与金融杂志》2,231-237。1983 年。

  • Rebonato R. 利率期权模型:理解,分析和使用异国利率期权模型。1998 年。Wiley。

  • D.,Brigo 和 Mercurio F.。“利率模型:理论与实践,第 2 版”。Springer Finance。2006 年。

  • Pelsser,Antoon。有效方法估值利率衍生品。Springer。2000 年。

  • Brace,A.,D. Gatarek 和 M Musiela。“利率动态市场模型”。《数学金融》。第 7 卷,第 2 期,127-154。1997 年。

  • Jarrow,Robert A.,和 Stuart Turnbull。“定价金融证券信用风险衍生品”。《金融杂志》。第 50 卷。1995 年 3 月。

  • Jarrow,R.,D. Lando 和 S. Turnbull。“信用风险利差期限结构的马尔可夫模型”。《金融研究评论》。第 10 卷。481-523。1997 年。

  • Duffie,D.,和 K. Singleton。“可违约债券期限结构建模”。《金融研究评论》。第 12 卷。687-720。1999 年。

  • 默顿,罗伯特 C.。“关于公司债务定价:利率风险结构”。《金融杂志》。第 29 卷,第 2 期,449-470 页。1974 年 5 月。

第三章

  • Glasserman,Paul。金融工程中的蒙特卡洛方法。Springer。2003 年。

  • Wilmott,Paul,Sam Howison 和 Jeff Dewynne。金融衍生品的数学:学生介绍。剑桥大学出版社。1995 年。

  • Cox,J.C.,Ross S.A.,和 Rubinstein M.。“期权定价:简化方法”。《金融经济学杂志》7(3),229。1979 年。

  • 布莱克,F.,和 M.斯科尔斯。“期权和公司负债的定价”。《政治经济学杂志》81(3),637-654。1973 年。

  • Kloeden P.E.,和 Platen E. Eckhard。随机微分方程的数值解(随机建模和应用概率)。Springer。1992 年。

第四章

  • 布莱克,Fischer 和 Myron Scholes。“期权和公司负债的定价”。《政治经济学杂志》81(3),637-654。1973 年。

  • Box,G.E.P.,和 Mervin E. Muller。关于生成随机正态偏差的注释。《数理统计学年鉴》。第 29 卷,第 2 期,610-611 页。1958 年。

  • Higham,Desmond。金融期权估值导论:数学,随机和计算。剑桥大学出版社。2004 年。

  • Wilmott,Paul。Paul Wilmott 量化金融,第 2 版。Wiley。2006 年。

第五章

  • 加曼,M.B.,和 S.W Kohlhagen。“外币期权价值”。《国际货币与金融杂志》2,231-237。1983 年。

第六章

  • Brace,A.,D. Gatarek 和 M Musiela。“利率动态市场模型”。《数学金融》。第 7 卷,第 2 期,127-154。1997 年。

  • Pelsser,Antoon。有效方法估值利率衍生品。Springer,2000 年。

第七章

  • 默顿,罗伯特 C.。“关于公司债务定价:利率风险结构”。《金融杂志》。第 29 卷,第 2 期,449-470 页。1974 年 5 月。

  • Jarrow,Robert A.,和 Stuart Turnbull。“定价金融证券信用风险衍生品”。《金融杂志》。第 50 卷。1995 年 3 月。

附录 A

  • Press,William H.,Saul A. Teukolsky,William T. Vetterling 和 Brian P. Flannery。“数字配方:科学计算的艺术,第 3 版”。剑桥大学出版社。2007 年。
posted @ 2024-05-15 15:26  绝不原创的飞龙  阅读(10)  评论(0编辑  收藏  举报