如何编写金融合约

第1章 引论

第2章 开始

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

第2.1节 一个简单合约

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

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

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

第2.2节 对于合约进行组合

所以说, zcb让我们能够构建一个简单合约. 我们也可以组合合约以制作更大的合约. 这样一种组合形式的良好例子是and, 其类型为: and:: ContractContractContract 使用and我们可以定义c3, 一个牵涉两个支付的合约: c2 , c3 :: Contract c2 = zcb t2 200GBP c3 = 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' ( give c2 ) 到目前为止, 我们的每个定义都在定义新的合约 (c1, c2, 等等). 定义新的组合子 (构建合约的函数) 也是相当容易的. 例如, 我们可以按照如下方式定义andGive: andGive :: ContractContractContract andGivecd = c 'and' ( gived ) 现在我们给出c4的另外一个定义 (比之前更加简单): c4 = c1 'andGive' c2 这种定义新的组合子, 然后将其像内置函数一样使用的能力, 对于函数式程序员是相当常规的, 但对于金融工程师并非如此.

第3章 构建合约

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

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

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

konst :: a Obsa ( konstx ) 是一个在任何时间都具有值x的量. lift :: ( ab ) Obsa Obsb ( liftfo ) 是可观察量, 其值为应用f于可观察量o的值的结果. lift2 :: ( abc ) Obsa Obsb Obsc ( lift2 f o1 o2 ) 是可观察量, 其值为应用f于可观察量 o1 o2 的值之结果. date :: ObsDate 日期为s时可观察量date的值就是s. instance Numa Num ( Obsa ) 所有的数值运算都可以提升至Obs类型. 实现颇为简单, 使用lift lift2 即可. 图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 : Contract PR (E1) Ek zero = K (0) (E2) Ek one k2 = exchk ( k2 ) (E3) Ek givec = Ek c (E4) Ek o 'scale' c = V o Ek c (E5) Ek c1 'and' c2 = Ek c1 + Ek c2 (E6) Ek c1 'or' c2 = max ( Ek c1 , Ek c2 ) (E7) Ek condo c1 c2 = if ( V o , Ek c1 , Ek c2 ) (E8) Ek whenoc = disck ( V o , Ek c ) (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 ) = ( give c1 ) 'or' ( give c2 ) 实际上, 第一个声明为真, 而第二个声明为假, 但是我们该如何确切地知道呢? 答案: 我们对比它们的估值语义, 将见于第4.6节.

第4.1节 价值过程

V : Obsa PRa V konstx = K (x) V date = date V liftfo = lift ( f, V o ) V lift2 f o1 o2 = lift2 ( f, V o1 , V o2 ) 图5. 可观察量的估值语义

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

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

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

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

第4.2节 从合约到过程

以下原语依赖于特定模型. exchk () : Currency PR disck ( , ) : PR𝔹 × PR PR snellk ( , ) : PR𝔹 × PR PR absorbk ( , ) : PR𝔹 × PR PR 图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) exch k2 ( k1 ) exch k3 ( k2 ) = exch k3 ( 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) exch k1 ( k2 ) disc k2 ( o,p ) = disc k1 ( o, exch k1 ( 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 ) 通过取等式左边的含义, 我们得到 Ek LHS = c1 + max ( c2 , c3 ) = max ( c1 + c2 , c1 + c3 ) = Ek RHS 其中c1=Ekc1, 诸如此类. 以类似的方式, 我们可以论证以下看似令人信服的等式实则是错误的: give ( c1 'or' c2 ) =? ( give c1 ) 'or' ( give c2 ) 证明是常规的, 但要义在于观察到 max ( a,b ) max ( a , b ) 回到现实世界, 要义在于左侧将选择交给了对手, 而右侧的选择则是由合约的持有者作出的.

我们的组合子满足丰富的一集等式, 例如以上关于andor的等式. 其中一些等式有着副条件, 例如: scaleo ( c1 'or' c2 ) = ( scaleo c1 ) 'or' ( scaleo c2 ) 成立仅当o0, 这恰好出于和giveor不能交换相同的原因. 保持耐心! o0是什么意思? 我们这里的意思是o在任何时间都为正. 更一般地, 合约之间不仅能够建立等式, 合约之间还存在着一种简单的序概念, c1c2读作c1主宰c2: c1c2, 如果在任意的时刻都不会出现获取c2的倾向大于获取c1的倾向. 这个序被定义为简单比较两个合约的价值过程; 也就是说, c1c2当且仅当Ec1Ec2. 一个价值过程大于另一个价值过程, 当且仅当其在世界的任意状态下都大于. (这个序仅仅是一个偏序.)

等式, 例如上文所给出的, 可以在求值引擎中作为优化变换使用. 一个合约编译器可以使用这些恒等式对于合约进行变换, 其以价值过程的中间语言进行表达 (见第4章的引论), 以将其转换为能够更高效地进行求值的形式.

第4.7节 总结

以上完成了我们对抽象估值语义的描述. 从编程语言的角度来看, 一切都相当常规, 包括我们的证明. 但我们要强调, 在金融行业中, 在这一抽象层次上进行形式化证明是极为罕见的. 我们已经命名并驯服了那些复杂的原语 (disc, exch等): 它们必须满足的定律为我们提供了一种方法, 使我们能够证明关于合约的恒等式, 而无需深入了解随机变量的诸多细节. 其中的数学细节相当晦涩, 请相信我们!

第5章 实现

我们的估值语义不仅仅是一个抽象的存在. 我们还可以将图4和图5视为从我们的合约语言到一种更低层次的过程语言的翻译, 该过程语言的组合子即为图6和图7中的原语. 然后, 我们可以利用诸如(A1)-(A5)之类的恒等式来优化过程层面的描述. (我们使用了数十条这样的恒等式作为变换规则.) 最终, 我们所需要做的就是实现这些过程层面的原语, 这样我们就能够对任意合约进行估值.

关键的决策当然是如何实现一个价值过程. 价值过程必须以显式的方式表示对未来的不确定性. 对这种不确定性进行建模的方法有很多种. 为了具体起见, 我们将简单地选择Ho-Lee模型, 并使用格方法来对合约进行估值 [Ho and Lee, 1986]. 我们选择该模型和数值方法是因为它们在技术上的简洁性和历史上的重要性, 但本节的大部分内容同样适用于其他模型 (例如Black-Derman-Toy). 更换数值方法 (例如换成蒙特卡罗方法) 将带来更大的变动, 但我们的语言及其语义 (第1-4节) 不会受到任何影响. 事实上, 完全可以对同一份合约的不同部分使用不同的数值方法.

第5.1节 利率模型

在典型的Ho-Lee数值方案中, 利率的演化由一个格 (或称重组树) 表示, 如图8所示. 树的每一列代表一个离散的时间步, 时间从左向右递增. 时间零点代表现在. 与离散模型一样, 时间步长的选取是一个问题; 我们在此不做进一步讨论, 但顺便指出, 各时间步的长度不必是均匀的.

在树的每个节点上关联着一个单期短期利率, 以下简称为利率. 我们知道今天的利率, 因此树的第一列只有一个元素. 然而, 到第一个时间步结束时, 利率会演化为何值存在一定的不确定性. 这通过在第二列中设置两个利率值来表达; 其含义是利率将以相等的概率演化为这两个值之一. 在第三个时间步, 利率再次分裂, 但下行/上行路径与上行/下行路径会合, 因此第三列中只有三个利率值, 而非四个. 这就是该结构被称为格的原因; 它使得树的宽度仅随时间线性增长, 从而保证了整个方案在计算上的可行性. 当然, 该树只是连续过程的一种离散近似; 其重组特性只是出于效率考虑的一种选择. 我们用Rt表示时间步数为t的利率向量, 对于该向量的第i个元素, 我们记以Rt,i, 此索引自零开始计. 因此, 例如R2,1=5%. 图8中的数字是不切实际地规整: 在更复杂的利率模型中, 它们不会等间距分布, 而只是在每列中单调分布.

第5.2节 价值过程

利率模型就介绍到这里. 价值过程由一个与利率演化形状完全相同的格来建模, 不同之处在于每个节点上存储的是一个价值而不是一个利率. 图9展示了我们最常用的零息债券的价值过程树: c7 = when ( att ) ( scale ( konst10 ) ( oneGBP ) ) 其以GBP求值. 使用我们的估值语义, 我们有 EGBP c7 = disc£ ( date=t , K (10) exch£ (£) ) = disc£ ( date=t , K (10) K (1) ) = disc£ ( date=t , K (10) ) 在图中, 我们假定时间t是时间步数3. 因此,

第6章 操作语义

到目前为止, 我们只讨论了合约的估值问题. 另一个适合自动化辅助的操作是合约的执行, 或者说管理. 银行持有数以千计的合约, 要记住何时支付款项, 何时收取款项, 以及何时可以做出选择 (对于期权而言), 绝非易事.

合约会随时间演化. 例如, 一个最初为 when ( att ) ( c1 'or' c2 ) 的合约, 在时间t到来时会演化为(c1'or'c2), 进而演化为c1c2. 这种演化过程让人联想到程序在执行过程中的演化方式. 实际上, 我们可以将执行合约视为类似于运行程序. 编程语言领域的学者使用操作语义来精确描述运行中程序的演化过程, 我们也可以对合约做同样的事情.

篇幅所限, 此处无法进行完整的讨论. 这里只需指出, 描述合约如何随时间演化的规则相对容易给出, 而在自动化系统中实现这些规则也并不复杂. 驱动合约估值引擎的同一份合约描述, 同样可以驱动合约管理引擎. 与估值引擎的情况一样, 管理引擎只需在添加新的原语组合子时才需要增强——它能够自动处理由已知原语构建的任意合约.

不仅如此, 一个部分执行的合约的状态本身仍然是一个合约项, 因此可以被输入到合约估值引擎中. 这样, 银行可以在任何时候对其全部部分执行合约的账簿进行估值, 只需将它们输入估值引擎即可.

第7章 将我们的工作置于上下文之中

乍看之下, 金融合约与函数式编程似乎没有太大关系. 然而令我们惊喜地发现, 许多在编程语言的设计, 语义和实现中有用的洞见, 可以直接应用于合约的描述, 估值和管理. 最初的想法是将函数式编程应用于一个现实问题, 并将我们得到的程序与现有的命令式版本进行比较——但最终我们对如何描述和评估合约进行了一次根本性的重新思考.

尽管在领域特定编程语言方面已有大量工作 (参见[Hudak, 1996, van Deursen et al., 2000]的综述), 我们的工作实际上是对金融合约进行形式化描述的唯一尝试. 一个例外是在CWI开发的RISLA语言 [van Deursen and Klint, 1998], 这是一种面向对象的金融合约领域特定语言. RISLA是为面向对象框架设计的, 与我们的系统相比, 它似乎更具状态性而较少声明性.

我们将我们的设计呈现为嵌入在Haskell中的组合子库, 事实上Haskell已被证明是一种出色的宿主语言, 非常适合对库设计和各种实现选择进行原型开发. 然而, 我们的设计绝不是Haskell特有的. 最大的收益来自于以声明式方法描述合约. 碰巧我们也使用了函数式语言来实现合约语言, 但这在某种程度上是附带的. 它同样可以被实现为一种独立的领域特定语言, 使用领域特定的编译器技术.

许多应用领域使用一种或多种ad hoc的语言来指定领域对象, 尽管这类语言通常被视为数据文件格式而非语言. 我们在此描述的工作是语言学方法强大能力的一个很好的例证: 我们不仅能够描述比任何商业系统都更为丰富的合约族, 而且语言结构直接导向了一个模块化且健壮的软件框架. 有原则的思考能带来实际的回报.

致谢

我们衷心感谢Lombard Risk Systems Ltd的John Wisbey, Jurgen Gaiser-Porter和Malcolm Pymm 的合作. 他们投入了大量时间, 向本文作者之一 (Peyton Jones) 传授金融合约的奥秘以及Black-Derman-Toy估值模型. Jean-Marc Eber热忱感谢Philippe Artzner的许多有益讨论, 以及Pierre Weis的大量建议. 我们还要感谢Conal Elliott, Jeremy Gibbons, Andrew Kennedy, Stephen Jarvis, Andy Moran, Chris Okasaki, Norman Ramsey, Colin Runciman, Ganesh Sittampalam, David Vincent以及ICFP的审稿人们提供的宝贵反馈.