如何编写金融合约

第1章 引论

第2章 开始

本节我们将会非形式化地引入我们对于合约的记号, 并展示我们如何根据较为简单的合约构建更为复杂的合约. 整个文章我们都会使用函数式语言Haskell.

第2.1节 一个简单合约

考虑以下简单合约, 或许可以充当Richard的礼物: receive £100 on 13th February 2003. (这种形式的合约在业界被称为零息折扣债券 (zero-coupon discount bond).) 我们可以描述这个合约, 称之为c1, 如下:c1::Contractc1=zcbt1100GBP图1总结了整篇文章我们对于变量所使用的记号约定, 例如这个定义里的c1t1.

c1定义中所使用的组合子zcb具有如下类型:zcb::DateDoubleCurrencyContract

zcb的第一个参数是一个Date, 其刻画了时间方面的一个特定时刻 (即日期和时间). 我们提供了一个函数mkDate, 其将表达以一个友好的字符串的date转换为一个Date.mkDate::StringDate现在我们可以定义Richard的2003年和2004年的生日如下:t1,t2::Datet1=mkDate"0800 GMT 13 Feb 2003"t2=mkDate"0800 GMT 13 Feb 2004"

第2.2节 对于合约进行组合

所以说, zcb让我们能够构建一个简单合约. 我们也可以组合合约以制作更大的合约. 这样一种组合形式的良好例子是and, 其类型为:and::ContractContractContract使用and我们可以定义c3, 一个牵涉两个支付的合约:c2,c3::Contractc2=zcbt2200GBPc3=c1'and'c2也就是说, 如果Richard持有合约c3, 那么他会在2003年生日受益于£100的支付, 在2004年生日受益于£200的支付.

一般而言, 我们可以描述的合约是两方之间的, 即合约的持有者, 以及交易对手. 尽管有圣经教导在先 (Acts 20.35), 默认情况下还是合约的持有者接受支付, 并作出选择, 这在合约中进行描述. 这种情况可由give组合子反转:give::ContractContract合约givec不过就是c的权利和义务反转, 而其精确含义我们将会在第4.2节揭晓. 的确, 当两方就合约达成一致时, 一方获得了合约c, 而另一方获得了合约givec; 每一方都是另一方的对手方. 例如, c4是一个合约, 其持有者在时间t1接受£100, 而在时间t2支付£200:c4=c1'and'(givec2)到目前为止, 我们的每个定义都在定义新的合约 (c1, c2, 等等). 定义新的组合子 (构建合约的函数) 也是相当容易的. 例如, 我们可以按照如下方式定义andGive:andGive::ContractContractContractandGivecd=c'and'(gived)现在我们给出c4的另外一个定义 (比之前更加简单):c4=c1'andGive'c2这种定义新的组合子, 然后将其像内置函数一样使用的能力, 对于函数式程序员是相当常规的, 但对于金融工程师并非如此.

第3章 构建合约

zero::Contractzero是一个没有权利且没有义务的合约.one::CurrencyContract如果你获得了(onek), 那么你立即可以收到货币k的一个单位.give::ContractContract获得(givec)就是获得所有的c的权利作为义务, 所有的义务作为权利.注意到对于A方和B方之间的双边合约q而言, A获得q可以推出B获得(giveq).and::ContractContractContract如果你获得了(c1'and'c2), 那么你立即获得了c1c2.or::ContractContractContract如果你获得了(c1'or'c2), 那么你必须立即从c1c2中选择一个获得.cond::ObsBoolContractContractContractscale::ObsDoubleContractContractwhen::ObsBoolContractContractanytime::ObsBoolContractContractuntil::ObsBoolContractContract图2. 定义合约的原语

我们已经完成了我们的非正式介绍. 本章我们将给出完整的原语集, 并展示各种各样的合约是如何通过这些原语构建的. 处于参考的目的, 图2给出了所有合约上的原始组合子; 我们将会按需引入这些原语.

第3.1节 获取日期和可观察量

konst::aObsa(konstx)是一个在任何时间都具有值x的量.lift::(ab)ObsaObsb(liftfo)是可观察量, 其值为应用f于可观察量o的值的结果.lift2::(abc)ObsaObsbObsc(lift2fo1o2)是可观察量, 其值为应用f于可观察量o1o2的值之结果.date::ObsDate日期为s时可观察量date的值就是s.instanceNumaNum(Obsa)所有的数值运算都可以提升至Obs类型. 实现颇为简单, 使用liftlift2即可.图3. 可观察量上的原语

图2给出了对于每个组合子的自然语言描述, 尽管相当精确. 为了做到这一点, 它使用了两个我们必须预先引入的概念: 获取日期和可观察量.

我们的语言描述了什么是一个合约. 然而, 合约对于持有者而言的后效依赖于合约获得的日期, 也就是获取日期 (acquisition date). (使用持有者的后效, 我们指的是合约施加于合约持有者的支付, 权利和义务.) 例如, 合约receive £100 on 1 Jan 2000 and receive £100 on 1 Jan 2001若获取时间晚于2000年1月1日则会价值低上不少, 因为根据定义, 任何在获取日期之前截止的权利和义务应该直接抛弃.

第二个基础概念是可观察量. 一个真实合约往往依赖于可测量的量. 例如, 一个合约可能会说receive an amount in dollars equal to the noon Centigrade temperature in Los Angeles; 或者是

第3.2节 折扣债券

第3.3节 可观察量和缩放

第3.4节 期权合约

第3.5节 限制合约 (limit contracts)

第3.6节 总结

第4章 估值

Ek:ContractPR(E1)Ekzero=K(0)(E2)Ekonek2=exchk(k2)(E3)Ekgivec=Ekc(E4)Eko'scale'c=VoEkc(E5)Ekc1'and'c2=Ekc1+Ekc2(E6)Ekc1'or'c2=max(Ekc1,Ekc2)(E7)Ekcondoc1c2=if(Vo,Ekc1,Ekc2)(E8)Ekwhenoc=disck(Vo,Ekc)(E9)(E10)图4. 合约的复合性估值语义

我们现在拥有一种丰富的语言, 可用于描述金融合约. 这在人与人之间的交流中已经非常有用——金融业界正缺乏这样一种精确的表示法. 但除此之外, 精确的描述还适用于各种自动处理. 我们有望从单一的合约描述中, 生成法律文件, 图表, 日程表以及更多其他内容. 然而, 人们可能会对合约提出的最直接的问题是: 它价值几何? 也就是说, 为了拥有这份合约, 我愿意支付多少钱? 这正是我们现在要转向的问题.

我们将通过两个层次来表达合约估值:

抽象估值语义. 首先, 我们将展示如何将使用我们的语言编写的任意合约, 转化为一个价值过程 (value process), 并要展示对于这些过程的一组操作. 这些过程与金融专家所使用的数学和随机机制直接对应.

具体实现. 过程是一个抽象的数学值. 为了让计算机利用过程进行计算, 我们必须以某种方式对其进行表示——这是从抽象语义向具体实现迈出的一步. 一个实现将由一个金融模型以及某种离散的数值方法组成. 如今有大量不同的金融模型被投入使用 (例如Black-Scholes, Ho-Lee, 等等); 但在业界, 只有三大类数值方法被广泛使用: 偏微分方程 [Willmot et al., 1993], 蒙特卡洛 [Boyle et al., 1997] 和格方法 [Cox et al., 1997].

这种方法强烈让人联想到编译器典型的构建方式. 程序首先被翻译成一种低级但与机器无关的中间语言; 许多优化都会在这个层面上进行; 然后程序被进一步翻译成目标处理器 (Pentium, Sparc, 诸如此类) 的指令集.

以类似的方式, 我们可以将一个合约转换为一个价值过程. 在计算该过程的价值之前, 对这个中间表示应用保持语义不变的优化转换. 最后的这个步骤可以通过解释执行的方式来完成, 或者也可以设想生成特化的代码, 在运行时执行估值.

事实上, 我们的抽象语义可以作为判断两个合约是否相同的参考模型. 例如, 存在两个声明:c1'and'(c2'or'c3)=(c1'and'c2)'or'(c1'and'c3)give(c1'or'c2)=(givec1)'or'(givec2)实际上, 第一个声明为真, 而第二个声明为假, 但是我们该如何确切地知道呢? 答案: 我们对比它们的估值语义, 将见于第4.6节.

第4.1节 价值过程

V:ObsaPRaVkonstx=K(x)Vdate=dateVliftfo=lift(f,Vo)Vlift2fo1o2=lift2(f,Vo1,Vo2)图5. 可观察量的估值语义

K:aPRa过程K(x)被定义为在任意时刻都具有值x.date:PRDate过程date以日期作为其值.cond:PRBoolPRaPRaPRalift:(ab)PRaPRb逐点应用函数于参数过程.lift2:(abc)PRaPRbPRc将两个参数过程以函数逐点组合.+,,:PRPRPR加起或乘起两个过程, 等等. 等价于lift2(+), 等等.图6. 过程原语

定义1. 类型a上的价值过程p是一个从时间到类型为a的随机变量的(完全)函数. 随机变量p(t)描述了p在时间t处的可能值. 我们写下非正式的类型定义PRa=DateRVa(对于语义层次的类型我们使用花体.) 因为我们需要与相同潜在空间 (从技术上说, 是filtration) 上的不同过程打交道, 这样的价值过程的更精确描述是给定filtration的适应随机过程. 此类过程配备了一套复杂的数学理论 [Revuz and Yor, 1991, Musiela and Rutkowski, 1997], 但计算机科学家可能对其并不熟悉, 因此我们仅提供非正式的直观的概念. 我们通常将价值过程简称为过程. 不过需要注意的是: 过程变量的含义与它们常规的计算机科学含义完全不同.

合约和可观察量都被建模为过程. 其潜在的直觉如下:

这些直觉对于理解本文的剩余内容是必要的.

第4.2节 从合约到过程

以下原语依赖于特定模型.exchk():CurrencyPRdisck(,):PR𝔹×PRPRsnellk(,):PR𝔹×PRPRabsorbk(,):PR𝔹×PRPR图7. 模型原语

那么,我们该如何由合约和可观察量得到过程呢? 图4给出了从合约到过程的完整转换, 而图5对可观察量做了同样的处理. 这些图看起来并不令人印象深刻, 但这也正是要义所在! 到目前为止的一切都在为这一点做铺垫; 我们的整个设计都是围绕着提供一个简单, 易处理, 模块化的估值语义这一愿望展开的. 让我们更仔细地看一下图4.

函数Ekc取了一个合约, 然后将其映射为了一个过程, 这个过程描述的是在每一时刻, 获取合约c以货币k计的价值. 例如, 描述give的等式(E3)说的是givec的价值过程不过就是Ekc的反转, Ekcc的价值过程. Aha! 反转是什么意思? 显然, 我们不仅需要价值过程的概念, 还需要一集能够施行于过程之上的运算. 反转一个过程就是这样的运算之一; 一个过程p的反转不过就是一个函数, 其将每个时间t映射至p(t)的negation. 提升所有实数上的运算以对于过程进行逐点操作是绝对直接的练习. (继而这需要我们对于随机变量进行negate, 不过这是简单的.) 我们需要其他诸多过程上的运算. 这些运算在图6和7中得到了总结, 但是我们将按需引入.

接着考虑等式(E5). 两个合约之and不过就是简单通过取两个价值过程之和来建模. 等式(E6)对于or组合子做了相同的时期. 又一次, 根据设计, 该组合子映射为了一个简单的数学运算max.

等式(E4)既漂亮又简单. 为了以时变可观察量o对于合约c进行缩放, 我们不过就是将合约c的价值过程和可观察量的价值过程相乘——记得我们将每个可观察量建模为一个价值过程. 我们将后者表达为Vo, 其在图5中以和Ekc非常类似的方式进行定义. 乍看上去这有些奇怪: 当缩放应用于c未来的支付和收取时, 我们该如何逐点缩放? 回忆一下, c的价值过程在时间t处给出了在t时获取c的价值. 那么, 如果这个价值是v, 获取相同的合约但所有的支付和收取都缩放以x的价值当然应该是vx. 我们在图2中对于scale的定义实际上直接由我们想要以简单方式表达其语义这一欲望所驱使. 简单的语义导致了简单的代数性质 (第4.6节).

第4.3节 汇率

图6中所定义的价值过程上的运算是一般性的——其与特定金融模型无关. 但是, 我们不能永远抛开金融模型. 图7中的原语是特定于金融合约的, 而它们在图4的剩余等式中使用. 考虑图4中的等式(E2). 它指出以货币k表达的货币k2的一个单位的价值过程, 不过就是k2k之间的汇率过程, 即exchk(k2) (图7). 我们从哪里获得这些汇率过程? 当涉及到具体实现时, 我们将需要一些关于汇率未来演变的(数值)假设, 但目前而言, 将汇率过程视为原语就足够了. 然而, 它们之间存在着重要的关系! 特别是:(A1)exchk(k)=K(1)(A2)exchk2(k1)exchk3(k2)=exchk3(k1)也就是说, 一种货币与其自身的汇率过程处处为一; 并且, 不论是直接将k1换成k3, 还是将k1藉由某个中间货币k2换成k3, 都没有区别. 这些是无套利条件的特别情形.

你可能还会好奇, 每个旅行者在外汇柜台都会遇到的买卖价差 (bid-offer spread) 怎么不见了. 为了保持技术上的易处理性, 金融理论在大多数情况下假设不存在任何价差: 通常先计算一个公允价格, 最后再加入利润空间. 产生价差的是后者, 而我们的建模仅适用于前者.

第4.4节 利率

接下来,考虑等式(E8). 一旦可观察量o变为True, 或者是在获取(whenoc)的时刻o就是True, 那么合约(whenoc)就立即获得潜在的合约c. 在oTrue的状态下, (whenoc)的价值因而和c的价值相同. 那么在oFalse的状态下, (whenoc)的价值是什么呢? 为了回答这个问题, 我们需要对于利率的未来演变的描述, 即一个利率模型.

让我们考虑一个具体的例子:c=when(att)(scale(konst10)(oneGBP))其中t是今天之后一年的时刻. 潜在的合约在其获取时会立即支付£10; whent时获取它. 于是, ct时的价值就是£10. 然而, 在t之前, 它并不值这么多. 如果我期望利率在下一年平均相当于10%, 那么对于今日的c的公平价格应该是大约£9.

正如原语exch包裹了对于未来汇率演变的假设, 原语disck(o,p)包裹了利率演变 (图7). 这里的o是一个布尔值过程, 其定义了一个区域, 我们将其称为获取区域 (acquisition region). 在该区域里——即在o具有值True的状态下——disck(o,p)等于p. 而在区域之外, disck(o,p)的值应该由计算p折现期望值 (discounted expected value)得到. 例如, 设p在任何时候(的价值)都等于£100, 而在任何1 Jan 2003之后的时间o都为True. 那么, disc£(o,p)在1 Feb 2003时的价值是100. 然而, 其在1 Jan 2002的价值则取决于英镑利率 (sterling interest rate): 我们必须要计算在1 Jan 2003时取得£100对应于1 Jan 2002时的值.

就像exch一样, 存在一些性质对于任意的无套利金融模型而言都应该满足. 尤其是:(A3)disck(K(True),p)=p(A4)exchk1(k2)disck2(o,p)=disck1(o,exchk1(k2)p)(A5)disck(o,p1+p2)=disck(o,p1)+disck(o,p2)第一个等式是说, 如果获取区域是整个空间, 那么disc应该是恒等函数; 第二个等式是说, 不同货币的利率演化应该与汇率演化假设兼容. 第三个等式经常作为优化自右向左使用: 与其对于两个随机变量单独施行折现, 将两个随机变量相加然后对于结果进行折现来得更快. {原注: 受过金融教育的读者应该注意到这里我们隐式假定了完备 (complete)市场.} 正如一个优化编译器, 我们可以使用像这样的等式对于我们的合约(的意义)进行变换, 将其转化为执行更为快速的形式.

尽管如此, 我们需要小心谨慎. 以下是一个看似令人信服的性质, 但是并不成立:disck(o,max(p1,p2))=max(disck(o,p1),disck(o,p2))它之所以看上去令人信服, 是因为如果p1,p2是单个数字, 而disc是简单的乘性因子的话, 那么这就是成立的. 但是, p1p2是随机过程, 于是这个性质就不成立.

等式(E9)使用了snell算子以给出anytime的意义.

第4.5节 可观察量

我们只能对能够建模的可观察量进行合约估值. 例如, 只有在我们有洛杉矶气温的模型时, 才能对涉及洛杉矶气温的合约进行估值. 有些这样的可观察量显然需要单独的模型. 而另一些, 比如LIBOR利率和期货价格, 则可以自洽地建模为特定合约的价值. 我们在此省略所有细节; 图5仅给出了最简单的可观察量的语义. 然而这并不意味着不切实际. 利用我们的合约组合子, 仅凭这些简单的可观察量就可以写出范围很广的合约.

第4.6节 对于合约进行推理

现在我们准备好用我们的语义来回答在第4节开头提出的问题了. 首先, 下面这个等式是否成立?c1'and'(c2'or'c3)=(c1'and'c2)'or'(c1'and'c3)通过取等式左边的含义, 我们得到EkLHS=c1+max(c2,c3)=max(c1+c2,c1+c3)=EkRHS其中c1=Ekc1, 诸如此类. 以类似的方式, 我们可以论证以下看似令人信服的等式实则是错误的:give(c1'or'c2)=?(givec1)'or'(givec2)证明是常规的, 但要义在于观察到max(a,b)max(a,b)回到现实世界, 要义在于左侧将选择交给了对手, 而右侧的选择则是由合约的持有者作出的.

我们的组合子满足丰富的一集等式, 例如以上关于andor的等式. 其中一些等式有着副条件, 例如:scaleo(c1'or'c2)=(scaleoc1)'or'(scaleoc2)成立仅当o0, 这恰好出于和giveor不能交换相同的原因. 保持耐心! o0是什么意思? 我们这里的意思是o在任何时间都为正.