2011. 6. 27. 09:39 quantlib/Implementation
4. Cash flows and coupons
"CASH IS KING," says a sign in the office of StatPro's CFO. Cash를 다루기 위해 QuantLib는 pricing뿐만 아니라 bonds와 interest-rate swaps와 같은 coupon-bearing instrument를 분석하는 것을 제공해야만 한다. 이번 챕터는 cash flows와 coupons을 모델링하는 클래스는 다룬다.
4.1 The CashFlow class
최상위 클래스는 CashFlow이며, 모든 cash flows를 위한 기본 인터페이스를 제공한다. 함수들은 cash flow의 date와 amount를 리턴한다. dates를 명시적으로 비교하는 것으로부터 어떤 것을 save하기 위해, 어떤 편리한 함수는 cash-flow 클래스들이 주어진 date에 이미 일어났는지 아닌지를 말해준다. 마지막으로 hook가 제공되므로 cash-flow 클래스들은 Acyclic Visitor pattern을 취할 수 있다; section 4.3에서 예제를 다룬다.
라이브러리 예전 버젼에서, 이러한 모든 함수는 CashFlow class에 선언되었었다. date와 관련된 함수들은 CashFlow로부터 상속되는 Event class로 따로 떨어져 나왔다. 이 두개의 인터페이스는 listing 4.1에서 보여진다.
라이브러리는 SimpleCashFlow class의 간단한 구현을 제공한다. 이것은 date와 ammount to be paid를 취하고 date와 amount 함수들로부터 각각의 결과를 리턴한다.
//Listing 4.1: Interfaces of the Event and CashFlow classes.
4.2 Interest-rate coupons
//Listing 4.2: Interface of the Coupon class.
Coupon class는 주어진 day-count convention을 기반으로 하거나 주어진 기간동안에 interest rate를 accruing하는 것을 기반으로 하는 cash-flow를 위한 parent class로써 사용될 수 있다. 이것은 추상 클래스이다; cash flow를 위한 추가적인 인터페이스를 정의하고 date calculation를 다루는 몇몇 함수를 구현한다.
추상 인터페이스는 coupon에 의해 accrued된 interest rate를 리턴하는 rate 함수를 포함한다; dayCounter와 accruedAmount 함수는 accrual에 사용되는 day-count convention과 주어진 date까지의 cash amount accrued를 각각 리턴한다.
purely abstract인 rate 함수를 선언하기 위한 선택은 명백하다. 하지만 다른 두 함수는 다르다. dayCounter는 Coupon 클래스의 데이터 멤버로서 day counter를 저장해야한다(section 3.1에서 TermStructure 클래스를 예로 언급함) 더욱이 nominal 함수(개념적으로 비슷한)는 abstract가 아니지만 같은 데이터 멤버를 기반으로 한다. As I'm all for abstract interfaces, I don't complain - 그러나 asymmetry는 불편한 점이 있다는 것을 인정한다.
accruedAmount 함수는 또 다르다; 이것은 wrong reason으로 abstract이다. 주어진 date까지의 accrual time과 notional에 곱의 결과를 갖는 rate 함수에 의해 default 구현을 갖을수도 있다. 이것을 abstract로 만드는 것은 amount 함수에 의해 rate 함수를 정의하는 파생 클래스들이 있기 때문에 앞서 말한 선택을 한것과 같은 것이다. 이러한 클래스들에서 accruedAmount는 더 효율적일 수도 있는 불확실한 grounds상에서 amount에 의해 정의된다. 그러나 올바른 선택은 default 구현을 Coupon 클래스에 추가하고 필요한 경우 override하는 것일 것이다. 이것은 다음 release에서 적용할 것이다.(amount 함수도 마찬가지이다)
인터페이스의 나머지는 concrete 함수들로 이루어 진다. 생성자는 data 집합을 취하고 데이터 멤버에 저장한다; 데이터는 nominal of the coupon, payment date, accrual time을 계산하는데 필요한 dates를 포함한다. 보통 이러한 dates는 accrual period의 시작과 끝이다. 선택한 day-count convention에 의해 두개의 dates(reference period의 시작과 끝)가 더 필요할 지도 모른다; section A.2에 자세히 나온다.
저장된 데이터를 위해 Coupon class는 inspector를 정의한다; 특히 payment date를 리턴하는 것은 CashFlow 인터페이스에 의해 필요한 date 함수를 구현한다. 더욱이 accrualPeriod와 accrualDays함수가 제공된다; listing에서 보여지듯이 이것들은 주어진 day-count convention과 dates를 사용한다.
두개의 notes가 있다.
첫번째로, dayCounter 함수에 관한 것으로, 관련 dates를 저장하는 것과 대응되는 함수를 abstract로서 선언하는 것의 대체안을 가지고 있다. 전에 I'm all for abstract interfaces라고 말한것과 같다; 이 경우 거의 모든 파생 클래스에게 dates를 데이터 멤버에 저장하도록 하는 것과 같은 inspectors를 구현하게 하는것을 강요하는 것은 좋은 생각이 아닌 것 같다.(coupon dates를 데이터 멤버에 저장할 필요가 없는 파생 클래스만이 기존의 coupon을 decorating할 수 있을 것이다.) 만약 당신이 좋아한다면, dayCounter를 concrete 함수로 만드는 것도 가능하다.
두번째로, 정보는 레포팅과 같은 목적을 위해서 필요하기 때문에, inspectors에 dates를 노출하는 것은 명백히 맞는 것이다. 그러나 이것은 누군가가 accrualPeriod롸 같은 더 상위의 함수에 의존하는 것 대신에 그것들을 계산하는데 사용할지도 모른다. 물론 접근을 제한하는 것이 불가능하기 때문에 우리가 할수 있는 것은 이 이슈를 문서화하는 것이다.
앞으로 accept 함수를 설명할 것이다.
4.2.1 Fixed-rate coupons
Coupon 인터페이스를 구현하는 concrete 클래스를 보자. 가장 간단한 것은 fixed-rate coupon을 모델링하는 것이다; FixedRateCoupon으로 불리고 구현은 listing 4.3과 같다.
// Listing 4.3: Sketch of the FixedRateCoupon class.
실제로 현재의 구현은 매우 간단하지는 않다(비록 간단한 compounding coupon으로 시작하지만).
나중에 다른 compounding rules를 지원하도록 일반화되었다.
생성자는 Coupon 생성자와 지불될 rate 그리고 accrual에 사용될 day-count convention을 인자로 취한다. listing에 있는 생성자는 simple rate를 취한다; 다른 생성자는 InterestRate 인스턴스를 취하기도 하지만 여기서는 생략하였다. nominal과 dates는 Coupon 생성자로 보내진다. 다른 인자는 데이터 멤버에 저장된다; 특히 rate(부동소수점으로 전달되는)는 InterestRate를 instantiate하는데 사용된다.
Coupon 인터페이스의 일부분(rate와 dayCounter 함수)은 저장된 값을 리턴하는 것으로 구현된다. 나머지 함수들(amount, accruedAmount)는 이용가능한 정보에 의해 구현된다. amount는 nominal과 coupon life동안에 compounded되는 rate를 곱하고 오직 accrued interest를 구하기 위해 nominal을 빼는 것에 의해 구할 수 있다. accrued amount는 비슷한 방식을 사용하지만 다른 accrual period를 사용한다.
base Coupon 클래스에서 amount 함수를 위한 default 구현을 포함할 수 있다는 것을 언급했었다; 이것은 nominal을 rate와 accrual time을 곱하면 된다.
[이것은 이미 이 경우에 구현이 된것같다 - 그러나 이것의 유용함에 대해서는 합리적인 의심을 할수 있을 것이다. 소프트웨어 디자인은 절대 쉽지 않나?]
4.2.2 Floating-rate coupons
FloatingRateCoupon 클래스는 대부분 소프트웨어의 상징적인 life이다. 이것은 간단하게 시작했고 더욱 복잡해 졌으며 현재 refactoring을 필요로 할지 모른다; 현재 구현은(listing 4.4) 여러개의 이슈를 포함하고 있다.(backward compatibility를 break하지 않고 수정을 한다면, release 2.0으로 가기 전까지 문제점을 가지고 갈것이다.)
첫번째 이슈는 이름 자체이다: FloatingRateCoupon은 coupon의 특정 타입을 의미한다, 즉 LIBOR rate와 같은 종류로 지불하는 것을 말한다. 그러나 이 클래스는 더욱 일반적이며 다른 종류의 rates(CMS-constant maturity swap과 같은것들)를 지불하는 coupons를 모델링할수 있다. 불행히도 다른 이름은 더욱 안좋다; 예를들면, 이 단락을 쓰는 동안 간단히 고려하는 것(VariableRateCoupon)은 rate의 정의는 이 경우가 아닌 value를 바꿀지도 모른다. 대체로 지금 이름을 바꾸는 것은 어떤 의미도 없다고 생각한다.
// Listing 4.4: Sketch of the FloatingRateCoupon class.
나의 투덜댐은 그만하고 구현을 보자. 생성자는 Coupon 클래스, day counter 그리고 interest-rate fixing과 관련된 여러 인자들에 의해 필요한 dates와 notional을 취한다. 이런 것들은 rate 계산(appendix A)과 fixing의 다른 details를 고려하면서 InterestRateIndex 클래스의 인스턴스를 포함한다; 말하자면 많은 fixing days, 체납금(arrears)에서 rate가 fixed되었든 아니든,와 optional gearing and spread. Coupon 생성자로 전달이 되지 않는 인자는 데이터 멤버에 저장된다; 인스턴스는 interest-rate index와 현재의 evaluation date의 observer를 등록한다. fixingDays와 inArrears 인자는 fixing date를 결정하는데 사용된다. gearing과 spread는 coupon이 rate(R = g * F + s)에 지불하도록 한다(g:gearing, F:fixing of the underlying index, s:spread).(이론적으로 negative gearing을 전달함으로서 reverse-floater coupons을 모델링할수 있지만, 실무에서 zero에서 implicit floor를 방치(neglect)하기 때문에 권할만하지 않다)
그러므로 생성자 signature의 선택은 이 클래스에 의해 모델링될 수 있는 coupon의 종류를 제한한다. single index의 fixing을 기반으로 하는 rate로 제한한다; 두개의 rates사이의 spread를 지불하는 것들은 제외한다. spread 클래스를 생성하는 것과 InterestRateIndex로부터 그것을 상속하는 것에 의해 FloatingRateCoupon 클래스를 세밀히 조사하도록 강요당할수 있다(bolted); 그러나 이것은 모델링되는 financial concepts를 맴핑하는 것과 클래스 계층을 break할지도 모른다. 왜냐하면 두개의 indexes사이의 spread는 그 자체로 index가 아니기 때문이다. 아니면 최소 이것은 보통 고려대상이 아니다.
다른 함수들은 CashFlow와 Coupon 인터페이스를 구현한다. floating-rate coupons가 fixed-rate보다 더 복잡하지만, amount 함수는 더 간단한 구현을 가지고 있다: 이것은 rate를 accrual time과 nominal에 의해 곱한다. 이것은 이것을 Coupon 클래스로 이동시키는 것을 지원하는 것처럼 보인다. accrualAmount 함수는 listing에는 없지만 다른 accrual time을 가진 비슷한 구현을 갖는다. 당분간, rate 함수는 skip할 것이다; 잠시후 구현에 대해 볼 것이다.
inspectors를 보자. Coupon 인터페이스에서 필요한 dayCounter를 제외하더라도 우리는 index, fixingDays, gearing 그리고 spread와 관련된 파라미터를 리턴하는 것들을 가지고 있다. 마지막으로 함수들은 비지니스 로직을 구현하는 것들을 정의한다.
첫번째 두개의 함수는 fixingDate와 indexFixing이다. 이것들은 충분히 직관적이다. fixingDate 함수는 체납에 coupon fixes가 있는지 아닌지를 확인하고, reference date를 선택하고(체납일 경우 coupon의 마지막 일자, 그렇지 않다면 시작일자), 필요한 fixing days로 backward한다; 휴일은 index calendar에 의해 skip한다. indexFixing 함수는 관련 일자에 fixing를 위해 저장된 index를 ask한다.
두 함수의 signature와 관련된 이슈가 있다. 생성자는 single index를 가정한것 처럼 두개의 함수는 single index fixing을 가정한다. 이것은 일반화의 상실을 낳는다; 예를들어, 여러 fixing dates의 집합에서 index의 average fixing을 지불하거나 coupon like의 sub-period 상에서 rates를 compound하는 coupons은 제외된다.
마지막 함수는 index fixing에서 가능한 convexity adjustment를 다룬다. adjustedFixing 함수는 rate 함수에 의해 쓰여지고 R = g · ˆF + s 공식을 adjusted fixing ˆF = (R − s)/g를 리턴하기 위해 도치시킨다. 이것의 구현은 취약한 점을 가지고 있다. 왜냐하면 rate와 fixing사이의 previous relationship에 의존하기 때문이다. 만약 파생 클래스가 이것을 수정한다면(예를들어 paid rate에 cap 혹은 floor를 더하는 것에 의해 - 이미 추가했다), 이 함수는 그에 맞게 수정되어져야만 한다.불행히, 이것은 프로그래머에게 남겨진다; 동시에 두개의 함수를 override하도록 강요하는 language feature는 없다.
convexityAdjestment 함수는 original fixing과 adjusted one의 차이를 취함으로서 adjustment alone을 리턴한다. 이것은 protected convexityAdjestmentImpl 함수에 위임함으로써 수행된다; 이것은 이전 구현의 나머지 부분이고 각각의 분리된 함수의 존재는 계산을 최적화할수 있도록 한다. 이것은 더이상 현재의 구현에 필요하지 않다; protected 함수는 public으로 inlined되었다.
이제 rate 함수를 보자. 라이브러리 이전버젼에서 다음과 같이 저장된 InterestRateIndex 인스턴스에 의해 제공되는 fixing을 기반으로 구현되었다.(현재의 convexityAdjustment 구현은 무한루프에 빠지게 만들지도 모른다.)
새로운 요구사항이 필요함에 따라 구현은 변경되어야만 했다: floating-rate coupon은 여러가지 방법으로 pricing될수 있다. 이것은 Instrument 클래스에서 보았던 이슈이다.
Instrument에서 했듯이, Strategy pattern을 사용했다. 그러나 여기서는 pattern의 구현이 다르다. context가 more specific하다; 우리가 원하는 결과를 알고 있었다. FloatingRateCoupon은 Instrument보다 더 많은 인터페이스를 갖는다. 이것은 불투명한 인자와 PricingEngine 클래스에서 사용되는 result strucutre를 피할수 있도록 만든다.
FloatingRateCouponPricer class는 listing 4.5에 있다.
// Listing 4.5: Sketch of the FloatingRateCouponPricer class.
rates를 리턴하는 함수를 선언하고 있다: swapletRate는 coupon rate, adjusted for convexity, gearing and spread를 리턴하고, capletRate는 index fixing상의 cap에 의해 지불되는 adjusted rate를 리턴하고, floorletRate는 floor와 같은 것을 리턴한다. initialize 함수는 pricer가 인자 리스트에 일부분으로써 다른 함수에 전달되지 않는 정보를 제공하는 전달된 coupon의 reference를 저장하도록 한다.
FloatingRateCoupon 클래스는 pricer 인스턴스를 취하고 그것을 coupon에 저장하는 setPricer함수를 정의한다(구현이 심플하지는 않다). 마지막으로 rate 함수는 listing 4.4에 있다: 저장된 pricer는 현재의 쿠폰으로 초기화되고 계산된 rate는 리턴된다.
초기화가 rate함수가 아니라 setPricer 함수에서 수행되는 것을 보았을 것이다. 이것은 같은 pricer 인스턴스가 여러 개의 쿠폰에서 사용될수 있기 때문이다. 그러므로 pricer가 결과를 ask할 때마다 현재의 coupon이 저장되어 있는지를 확인해야만 한다. 이전의 계산은 다른 reference에 저장되어 있을지도 모른다 - 기다리게 할지도 모른다(dangling).
물론 이 구현은 전체적으로 만족스럽지 못하다(이것은 최근 몇년동안 더욱 중요해 지고 있는 병행처리를 숨기고 있다. 현재 우리는 같은 pricer를 여러개의 coupon에 넘기지 못하고 그러므로 동시에 그것들을 평가하지 못한다). pricer 함수를 정의하는데 더 나은 디자인이 있을 것이다. 예를들면 다음과 같으며 반복되는 initialize 호출을 피할수 있다.
Rate swapletRate ( const FloatingRateCoupon& coupon ) ;
나는 이른 최적화의 guilty를 인정해야만 한다. 우리는 swapletRate와 다른 비슷한 함수들이 (floored coupon을 price하기 위해) 순서대로(in sequence) 호출될때, 각각의 초기화 함수들은 다른 rate 계산들에서 사용되는 accessory quantities를 미리 계산할 수 있다; we let that possibility drive the design. 결과적으로 대부분의 pricers는 initialize 함수에서 computation을 수행한다; 그러나 내 생각에 rate를 계산하기 위한 Texas two-step을 정당화하기에는 충분하지 않다.
pricer machinery에 의해 추가되는 복잡함을 신경쓰지 않으면서 특정 floating-rate coupon을 구현하기를 원한다면, FloatingRateCoupon으로부터 상속을 하고, 계산을 직접수행하기 위해 rate 함수를 override하고 pricers에 대해서는 잊어라. 이것은 나중에 필요하면 추가할수 있다.
4.2.3 Example: LIBOR coupons
floating-rate coupon의 가장 공통된 것은 LIBOR index에 의해 fixed된 rate를 지불한다는 것이다. 이러한 coupon의 최소 구현은 listing 4.6에 있다.
// Listing 4.6: Minimal implementation of the IborCoupon class.
오직 필요한 멤버는 생성자이다. 이것은 FloatingRateCoupon과 같은 인자를 취하지만 전달된 index의 type을 특화한다; 이것은 이 인자가 더욱 일반적인 InterestRateIndex보다는 IborIndex 클래스의 인스턴스의 포인터가 되도록 제한한다.(Ibor 이름은 EURIBOR, LIBOR과 같은 index의 공통 suffix이다). 모든 인자는 부모 클래스의 생성자로 보내진다; index는 특별한 처리가 필요하지 않다. 왜냐하면 파생 클래스로의 shared 포인터가 그것의 베이스 클래스의 포인터로 묵시적으로 upcast될수 있기 때문이다.
물론 실제 pricing work는 pricers에 의해 수행될 것이다. Listing 4.7은 그것들 중에 하나, BlackIborCouponPricer 클래스,의 구현의 일부를 보여준다; 이름 중에 Black의 의미는 adjustment를 계산하고 index fixing의 caps or floors를 위해 Black 모델을 사용한다는 것이다.
// Listing 4.7: Partial implementation of the BlackIborCouponPricer class.
생성자는 LIBOR rate의 변동성을 기술하고 그것을 저장하며 observer로서 그것을 등록하는 term structure의 handle을 취한다. 만약 그 coupon rate가 연체되지않고 cap과 floor가 정의되지 않는다면, 변동성은 사용되지 않는다; 이러한 경우 handle은 비어있거나 생성자 호출시 생략될 수 있다.
initialize 함수는 전달된 coupon이 IborCoupon으로 downcasting에 의해 올바른 타입을 갖는지 확인한다. 만약 맞다면 몇가지 사전 처리가 수행된다. 여기서 두개의 데이터 멤버에서 coupon gearing과 spread를 저장한다; 이것은 나머지 코드를 더욱 간결하게 작성할수 있도록 도와준다. swapletRate함수는 간단히 저장된 gearing과 spread를 adjusted rate로 적용하고 이것의 계산은 adjustedFixing 함수로 위임된다. 만약 coupon이 tenor(투자, 대출 등의 계약기간 중 현재 시점에서의 잔여기간을 말합니다.)의 시작에서 고정되면, adjusted rate는 underlying index의 fixing이다. 만약 coupon이 연체된다면, convexity adjustment가 계산되고 fixing에 추가될 것이다; 여기서 공식에 대한 구현을 보이지는 않을 것이지만 유효한 caplet volatility를 위한 check를 할 것이다. capletRate와 같은 다른 함수는 여기서 다루지 않을 것이다; 나중에 예제로 다룰 것이다.
이것이 간단하지만 clean한 예제이다 - par coupons를 사용하여 floating-rate notes를 pricing하는 법. 라이브러리는 이러한 practice를 따르거나 그렇지 않도록 하게 할 수 있다; 이러한 방법은 몇가지가 있다.
첫번째로 (listing 4.8) pricer machinery를 bypass하고 LIBOR coupon을 위한 rate 함수를 override하는 것이다. 만약 par coupons가 compilation flag를 셋팅하는 것에 의해 사용할 수 있고 coupon이 연체되지 않는다면(이 경우 여전히 pricer를 필요로 한다), built-in par-rate 계산이 사용된다; 다시 말하면 함수 호출이 base 클래스 함수로 보내진다면 계산은 pricer로 위임된다. 이것은 동작하지만 단점을 가지고 있다.
// Listing 4.8: Extension of the IborCoupon class to support par coupons.
반면 pricer를 coupon에 설정하게 되면 observable 효과를 누릴수 없게 된다.
어떤 다른 index fixing은 같은 날짜상에서 연체되지 않은 coupons fixing이나 연체가 된 것을 위해 사용될 수 있다. 이것은 parity relationships or worse, hedges를 버릴수(throw off) 있다.
더 좋은 방법은 par-coupon pricer를 명시적으로 구현하는 것이다(Listing 4.9). 이것은 Black pricer를 상속함으로 인해 이루어진다.
// Listing 4.9: Sketch of the ParCouponPricer class.
파생 pricer는 오직 adjustedFixing 함수를 override하면 된다; fixing을 위한 저장된 index를 asking하는 것 대신에 risk-free curve를 extract하고 그것을 par rate를 계산하는데 사용한다. 이것은 coupon 클래스에서 rate를 override하는 것보다 cleaner하고 같은 단점을 겪지 않게 되며 런타임시 선택할 수 있다.
이 방법은 여전히 한가지 문제을 갖지만; convexityAdjustment 함수는 par fixing과 index fixing(convexity 효과때문이 아닌) 사이의 difference를 리턴할 것이다(coupon's rate 함수를 override할 때 같은 문제에 직면하게 된다). 불행히, 이것을 어떻게 고칠지는 분명하지 않다 - 최소한 나에게- 비록 몇개의 가능성은 있지만. 하나는 par coupons를 detect하고 null adjustment를 리턴하기 위해 convexityAdjustment를 override하는 것이지만 이것은 연체 coupons에는 동작하지 않을 것이다. 다른 가능성은 함수 이름을 adjustment로 바꾸고 더욱 일반화 시키는 것이지만 이것은 정보를 잃게된다. Best는 convexity adjustment의 계산을 pricer에 위임하는 것이다; 이것은 fragility 이슈를 극복하는데도 도움이 된다.
4.2.4 Example: capped/floored coupons
Caps와 floors는 어떤 종류의 floating-rate coupon에도 적용할 수 있다. 이 프레임워크에서 이것은 FloatingRateCoupon으로부터 파생되는 클래스에 generic way로 특징을 추가하기를 원하는 것을 의미한다.
이러한 요구사항은 Decorator pattern을 사용하기를 제안하게 된다. CappedFlorredCoupon 클래스의 인터페이스는 listing 4.10에 있고 구현은 4.11.에 있다.
// Listing 4.10: Interface of the CappedFlooredCoupon class.
클래스는 베이스 FloatingRateCoupon 클래스의 포인터를 저장하고 필요한 행동을 추가하며 다른 곳에 저장된 객체의 함수를 호출하면서 Decorator pattern의 표준(canonical)을 따른다. 그러나 다른 언어와는 다르게 C++은 Decorator pattern을 구현하는 다른 방법을 제공한다: 다음과 같이 templates와 상속을 이용한다.
class template은 필요한 행동을 추가하거나 각각의 베이스 클래스의 함수를 호출하는 여러 클래스들(CappedFloored<IborCoupon> 또는 CappedFloored<CmsCoupon>과 같은)을 instantiate하는데 사용된다. 이때 기존의 것을 template 구현으로 대체할만한 강한 근거를 찾지 못했다; 그러나 CappedFlooredCoupon을 설명하는 동안 class template가 다르다(better or worse)는 것에 대해 살펴볼 것이다.
놀랍지 않게도, 생성자는 데이터 멤버에 FloatingRateCoupon 인스턴스를 decorate하고 저장하기 위해 취한다.
// Listing 4.11: Implementation of the CappedFlooredCoupon class.
4.1 The CashFlow class
최상위 클래스는 CashFlow이며, 모든 cash flows를 위한 기본 인터페이스를 제공한다. 함수들은 cash flow의 date와 amount를 리턴한다. dates를 명시적으로 비교하는 것으로부터 어떤 것을 save하기 위해, 어떤 편리한 함수는 cash-flow 클래스들이 주어진 date에 이미 일어났는지 아닌지를 말해준다. 마지막으로 hook가 제공되므로 cash-flow 클래스들은 Acyclic Visitor pattern을 취할 수 있다; section 4.3에서 예제를 다룬다.
라이브러리 예전 버젼에서, 이러한 모든 함수는 CashFlow class에 선언되었었다. date와 관련된 함수들은 CashFlow로부터 상속되는 Event class로 따로 떨어져 나왔다. 이 두개의 인터페이스는 listing 4.1에서 보여진다.
라이브러리는 SimpleCashFlow class의 간단한 구현을 제공한다. 이것은 date와 ammount to be paid를 취하고 date와 amount 함수들로부터 각각의 결과를 리턴한다.
//Listing 4.1: Interfaces of the Event and CashFlow classes.
4.2 Interest-rate coupons
//Listing 4.2: Interface of the Coupon class.
Coupon class는 주어진 day-count convention을 기반으로 하거나 주어진 기간동안에 interest rate를 accruing하는 것을 기반으로 하는 cash-flow를 위한 parent class로써 사용될 수 있다. 이것은 추상 클래스이다; cash flow를 위한 추가적인 인터페이스를 정의하고 date calculation를 다루는 몇몇 함수를 구현한다.
추상 인터페이스는 coupon에 의해 accrued된 interest rate를 리턴하는 rate 함수를 포함한다; dayCounter와 accruedAmount 함수는 accrual에 사용되는 day-count convention과 주어진 date까지의 cash amount accrued를 각각 리턴한다.
purely abstract인 rate 함수를 선언하기 위한 선택은 명백하다. 하지만 다른 두 함수는 다르다. dayCounter는 Coupon 클래스의 데이터 멤버로서 day counter를 저장해야한다(section 3.1에서 TermStructure 클래스를 예로 언급함) 더욱이 nominal 함수(개념적으로 비슷한)는 abstract가 아니지만 같은 데이터 멤버를 기반으로 한다. As I'm all for abstract interfaces, I don't complain - 그러나 asymmetry는 불편한 점이 있다는 것을 인정한다.
accruedAmount 함수는 또 다르다; 이것은 wrong reason으로 abstract이다. 주어진 date까지의 accrual time과 notional에 곱의 결과를 갖는 rate 함수에 의해 default 구현을 갖을수도 있다. 이것을 abstract로 만드는 것은 amount 함수에 의해 rate 함수를 정의하는 파생 클래스들이 있기 때문에 앞서 말한 선택을 한것과 같은 것이다. 이러한 클래스들에서 accruedAmount는 더 효율적일 수도 있는 불확실한 grounds상에서 amount에 의해 정의된다. 그러나 올바른 선택은 default 구현을 Coupon 클래스에 추가하고 필요한 경우 override하는 것일 것이다. 이것은 다음 release에서 적용할 것이다.(amount 함수도 마찬가지이다)
인터페이스의 나머지는 concrete 함수들로 이루어 진다. 생성자는 data 집합을 취하고 데이터 멤버에 저장한다; 데이터는 nominal of the coupon, payment date, accrual time을 계산하는데 필요한 dates를 포함한다. 보통 이러한 dates는 accrual period의 시작과 끝이다. 선택한 day-count convention에 의해 두개의 dates(reference period의 시작과 끝)가 더 필요할 지도 모른다; section A.2에 자세히 나온다.
저장된 데이터를 위해 Coupon class는 inspector를 정의한다; 특히 payment date를 리턴하는 것은 CashFlow 인터페이스에 의해 필요한 date 함수를 구현한다. 더욱이 accrualPeriod와 accrualDays함수가 제공된다; listing에서 보여지듯이 이것들은 주어진 day-count convention과 dates를 사용한다.
두개의 notes가 있다.
첫번째로, dayCounter 함수에 관한 것으로, 관련 dates를 저장하는 것과 대응되는 함수를 abstract로서 선언하는 것의 대체안을 가지고 있다. 전에 I'm all for abstract interfaces라고 말한것과 같다; 이 경우 거의 모든 파생 클래스에게 dates를 데이터 멤버에 저장하도록 하는 것과 같은 inspectors를 구현하게 하는것을 강요하는 것은 좋은 생각이 아닌 것 같다.(coupon dates를 데이터 멤버에 저장할 필요가 없는 파생 클래스만이 기존의 coupon을 decorating할 수 있을 것이다.) 만약 당신이 좋아한다면, dayCounter를 concrete 함수로 만드는 것도 가능하다.
두번째로, 정보는 레포팅과 같은 목적을 위해서 필요하기 때문에, inspectors에 dates를 노출하는 것은 명백히 맞는 것이다. 그러나 이것은 누군가가 accrualPeriod롸 같은 더 상위의 함수에 의존하는 것 대신에 그것들을 계산하는데 사용할지도 모른다. 물론 접근을 제한하는 것이 불가능하기 때문에 우리가 할수 있는 것은 이 이슈를 문서화하는 것이다.
앞으로 accept 함수를 설명할 것이다.
4.2.1 Fixed-rate coupons
Coupon 인터페이스를 구현하는 concrete 클래스를 보자. 가장 간단한 것은 fixed-rate coupon을 모델링하는 것이다; FixedRateCoupon으로 불리고 구현은 listing 4.3과 같다.
// Listing 4.3: Sketch of the FixedRateCoupon class.
실제로 현재의 구현은 매우 간단하지는 않다(비록 간단한 compounding coupon으로 시작하지만).
나중에 다른 compounding rules를 지원하도록 일반화되었다.
생성자는 Coupon 생성자와 지불될 rate 그리고 accrual에 사용될 day-count convention을 인자로 취한다. listing에 있는 생성자는 simple rate를 취한다; 다른 생성자는 InterestRate 인스턴스를 취하기도 하지만 여기서는 생략하였다. nominal과 dates는 Coupon 생성자로 보내진다. 다른 인자는 데이터 멤버에 저장된다; 특히 rate(부동소수점으로 전달되는)는 InterestRate를 instantiate하는데 사용된다.
Coupon 인터페이스의 일부분(rate와 dayCounter 함수)은 저장된 값을 리턴하는 것으로 구현된다. 나머지 함수들(amount, accruedAmount)는 이용가능한 정보에 의해 구현된다. amount는 nominal과 coupon life동안에 compounded되는 rate를 곱하고 오직 accrued interest를 구하기 위해 nominal을 빼는 것에 의해 구할 수 있다. accrued amount는 비슷한 방식을 사용하지만 다른 accrual period를 사용한다.
base Coupon 클래스에서 amount 함수를 위한 default 구현을 포함할 수 있다는 것을 언급했었다; 이것은 nominal을 rate와 accrual time을 곱하면 된다.
[이것은 이미 이 경우에 구현이 된것같다 - 그러나 이것의 유용함에 대해서는 합리적인 의심을 할수 있을 것이다. 소프트웨어 디자인은 절대 쉽지 않나?]
4.2.2 Floating-rate coupons
FloatingRateCoupon 클래스는 대부분 소프트웨어의 상징적인 life이다. 이것은 간단하게 시작했고 더욱 복잡해 졌으며 현재 refactoring을 필요로 할지 모른다; 현재 구현은(listing 4.4) 여러개의 이슈를 포함하고 있다.(backward compatibility를 break하지 않고 수정을 한다면, release 2.0으로 가기 전까지 문제점을 가지고 갈것이다.)
첫번째 이슈는 이름 자체이다: FloatingRateCoupon은 coupon의 특정 타입을 의미한다, 즉 LIBOR rate와 같은 종류로 지불하는 것을 말한다. 그러나 이 클래스는 더욱 일반적이며 다른 종류의 rates(CMS-constant maturity swap과 같은것들)를 지불하는 coupons를 모델링할수 있다. 불행히도 다른 이름은 더욱 안좋다; 예를들면, 이 단락을 쓰는 동안 간단히 고려하는 것(VariableRateCoupon)은 rate의 정의는 이 경우가 아닌 value를 바꿀지도 모른다. 대체로 지금 이름을 바꾸는 것은 어떤 의미도 없다고 생각한다.
// Listing 4.4: Sketch of the FloatingRateCoupon class.
나의 투덜댐은 그만하고 구현을 보자. 생성자는 Coupon 클래스, day counter 그리고 interest-rate fixing과 관련된 여러 인자들에 의해 필요한 dates와 notional을 취한다. 이런 것들은 rate 계산(appendix A)과 fixing의 다른 details를 고려하면서 InterestRateIndex 클래스의 인스턴스를 포함한다; 말하자면 많은 fixing days, 체납금(arrears)에서 rate가 fixed되었든 아니든,와 optional gearing and spread. Coupon 생성자로 전달이 되지 않는 인자는 데이터 멤버에 저장된다; 인스턴스는 interest-rate index와 현재의 evaluation date의 observer를 등록한다. fixingDays와 inArrears 인자는 fixing date를 결정하는데 사용된다. gearing과 spread는 coupon이 rate(R = g * F + s)에 지불하도록 한다(g:gearing, F:fixing of the underlying index, s:spread).(이론적으로 negative gearing을 전달함으로서 reverse-floater coupons을 모델링할수 있지만, 실무에서 zero에서 implicit floor를 방치(neglect)하기 때문에 권할만하지 않다)
그러므로 생성자 signature의 선택은 이 클래스에 의해 모델링될 수 있는 coupon의 종류를 제한한다. single index의 fixing을 기반으로 하는 rate로 제한한다; 두개의 rates사이의 spread를 지불하는 것들은 제외한다. spread 클래스를 생성하는 것과 InterestRateIndex로부터 그것을 상속하는 것에 의해 FloatingRateCoupon 클래스를 세밀히 조사하도록 강요당할수 있다(bolted); 그러나 이것은 모델링되는 financial concepts를 맴핑하는 것과 클래스 계층을 break할지도 모른다. 왜냐하면 두개의 indexes사이의 spread는 그 자체로 index가 아니기 때문이다. 아니면 최소 이것은 보통 고려대상이 아니다.
다른 함수들은 CashFlow와 Coupon 인터페이스를 구현한다. floating-rate coupons가 fixed-rate보다 더 복잡하지만, amount 함수는 더 간단한 구현을 가지고 있다: 이것은 rate를 accrual time과 nominal에 의해 곱한다. 이것은 이것을 Coupon 클래스로 이동시키는 것을 지원하는 것처럼 보인다. accrualAmount 함수는 listing에는 없지만 다른 accrual time을 가진 비슷한 구현을 갖는다. 당분간, rate 함수는 skip할 것이다; 잠시후 구현에 대해 볼 것이다.
inspectors를 보자. Coupon 인터페이스에서 필요한 dayCounter를 제외하더라도 우리는 index, fixingDays, gearing 그리고 spread와 관련된 파라미터를 리턴하는 것들을 가지고 있다. 마지막으로 함수들은 비지니스 로직을 구현하는 것들을 정의한다.
첫번째 두개의 함수는 fixingDate와 indexFixing이다. 이것들은 충분히 직관적이다. fixingDate 함수는 체납에 coupon fixes가 있는지 아닌지를 확인하고, reference date를 선택하고(체납일 경우 coupon의 마지막 일자, 그렇지 않다면 시작일자), 필요한 fixing days로 backward한다; 휴일은 index calendar에 의해 skip한다. indexFixing 함수는 관련 일자에 fixing를 위해 저장된 index를 ask한다.
두 함수의 signature와 관련된 이슈가 있다. 생성자는 single index를 가정한것 처럼 두개의 함수는 single index fixing을 가정한다. 이것은 일반화의 상실을 낳는다; 예를들어, 여러 fixing dates의 집합에서 index의 average fixing을 지불하거나 coupon like의 sub-period 상에서 rates를 compound하는 coupons은 제외된다.
마지막 함수는 index fixing에서 가능한 convexity adjustment를 다룬다. adjustedFixing 함수는 rate 함수에 의해 쓰여지고 R = g · ˆF + s 공식을 adjusted fixing ˆF = (R − s)/g를 리턴하기 위해 도치시킨다. 이것의 구현은 취약한 점을 가지고 있다. 왜냐하면 rate와 fixing사이의 previous relationship에 의존하기 때문이다. 만약 파생 클래스가 이것을 수정한다면(예를들어 paid rate에 cap 혹은 floor를 더하는 것에 의해 - 이미 추가했다), 이 함수는 그에 맞게 수정되어져야만 한다.불행히, 이것은 프로그래머에게 남겨진다; 동시에 두개의 함수를 override하도록 강요하는 language feature는 없다.
convexityAdjestment 함수는 original fixing과 adjusted one의 차이를 취함으로서 adjustment alone을 리턴한다. 이것은 protected convexityAdjestmentImpl 함수에 위임함으로써 수행된다; 이것은 이전 구현의 나머지 부분이고 각각의 분리된 함수의 존재는 계산을 최적화할수 있도록 한다. 이것은 더이상 현재의 구현에 필요하지 않다; protected 함수는 public으로 inlined되었다.
이제 rate 함수를 보자. 라이브러리 이전버젼에서 다음과 같이 저장된 InterestRateIndex 인스턴스에 의해 제공되는 fixing을 기반으로 구현되었다.(현재의 convexityAdjustment 구현은 무한루프에 빠지게 만들지도 모른다.)
Rate f = index_−>fixing(fixingDate());
return gearing_ * (f + convexityAdjustment(f)) + spread_;
새로운 요구사항이 필요함에 따라 구현은 변경되어야만 했다: floating-rate coupon은 여러가지 방법으로 pricing될수 있다. 이것은 Instrument 클래스에서 보았던 이슈이다.
Instrument에서 했듯이, Strategy pattern을 사용했다. 그러나 여기서는 pattern의 구현이 다르다. context가 more specific하다; 우리가 원하는 결과를 알고 있었다. FloatingRateCoupon은 Instrument보다 더 많은 인터페이스를 갖는다. 이것은 불투명한 인자와 PricingEngine 클래스에서 사용되는 result strucutre를 피할수 있도록 만든다.
FloatingRateCouponPricer class는 listing 4.5에 있다.
// Listing 4.5: Sketch of the FloatingRateCouponPricer class.
rates를 리턴하는 함수를 선언하고 있다: swapletRate는 coupon rate, adjusted for convexity, gearing and spread를 리턴하고, capletRate는 index fixing상의 cap에 의해 지불되는 adjusted rate를 리턴하고, floorletRate는 floor와 같은 것을 리턴한다. initialize 함수는 pricer가 인자 리스트에 일부분으로써 다른 함수에 전달되지 않는 정보를 제공하는 전달된 coupon의 reference를 저장하도록 한다.
FloatingRateCoupon 클래스는 pricer 인스턴스를 취하고 그것을 coupon에 저장하는 setPricer함수를 정의한다(구현이 심플하지는 않다). 마지막으로 rate 함수는 listing 4.4에 있다: 저장된 pricer는 현재의 쿠폰으로 초기화되고 계산된 rate는 리턴된다.
초기화가 rate함수가 아니라 setPricer 함수에서 수행되는 것을 보았을 것이다. 이것은 같은 pricer 인스턴스가 여러 개의 쿠폰에서 사용될수 있기 때문이다. 그러므로 pricer가 결과를 ask할 때마다 현재의 coupon이 저장되어 있는지를 확인해야만 한다. 이전의 계산은 다른 reference에 저장되어 있을지도 모른다 - 기다리게 할지도 모른다(dangling).
물론 이 구현은 전체적으로 만족스럽지 못하다(이것은 최근 몇년동안 더욱 중요해 지고 있는 병행처리를 숨기고 있다. 현재 우리는 같은 pricer를 여러개의 coupon에 넘기지 못하고 그러므로 동시에 그것들을 평가하지 못한다). pricer 함수를 정의하는데 더 나은 디자인이 있을 것이다. 예를들면 다음과 같으며 반복되는 initialize 호출을 피할수 있다.
Rate swapletRate ( const FloatingRateCoupon& coupon ) ;
나는 이른 최적화의 guilty를 인정해야만 한다. 우리는 swapletRate와 다른 비슷한 함수들이 (floored coupon을 price하기 위해) 순서대로(in sequence) 호출될때, 각각의 초기화 함수들은 다른 rate 계산들에서 사용되는 accessory quantities를 미리 계산할 수 있다; we let that possibility drive the design. 결과적으로 대부분의 pricers는 initialize 함수에서 computation을 수행한다; 그러나 내 생각에 rate를 계산하기 위한 Texas two-step을 정당화하기에는 충분하지 않다.
pricer machinery에 의해 추가되는 복잡함을 신경쓰지 않으면서 특정 floating-rate coupon을 구현하기를 원한다면, FloatingRateCoupon으로부터 상속을 하고, 계산을 직접수행하기 위해 rate 함수를 override하고 pricers에 대해서는 잊어라. 이것은 나중에 필요하면 추가할수 있다.
4.2.3 Example: LIBOR coupons
floating-rate coupon의 가장 공통된 것은 LIBOR index에 의해 fixed된 rate를 지불한다는 것이다. 이러한 coupon의 최소 구현은 listing 4.6에 있다.
// Listing 4.6: Minimal implementation of the IborCoupon class.
오직 필요한 멤버는 생성자이다. 이것은 FloatingRateCoupon과 같은 인자를 취하지만 전달된 index의 type을 특화한다; 이것은 이 인자가 더욱 일반적인 InterestRateIndex보다는 IborIndex 클래스의 인스턴스의 포인터가 되도록 제한한다.(Ibor 이름은 EURIBOR, LIBOR과 같은 index의 공통 suffix이다). 모든 인자는 부모 클래스의 생성자로 보내진다; index는 특별한 처리가 필요하지 않다. 왜냐하면 파생 클래스로의 shared 포인터가 그것의 베이스 클래스의 포인터로 묵시적으로 upcast될수 있기 때문이다.
물론 실제 pricing work는 pricers에 의해 수행될 것이다. Listing 4.7은 그것들 중에 하나, BlackIborCouponPricer 클래스,의 구현의 일부를 보여준다; 이름 중에 Black의 의미는 adjustment를 계산하고 index fixing의 caps or floors를 위해 Black 모델을 사용한다는 것이다.
// Listing 4.7: Partial implementation of the BlackIborCouponPricer class.
생성자는 LIBOR rate의 변동성을 기술하고 그것을 저장하며 observer로서 그것을 등록하는 term structure의 handle을 취한다. 만약 그 coupon rate가 연체되지않고 cap과 floor가 정의되지 않는다면, 변동성은 사용되지 않는다; 이러한 경우 handle은 비어있거나 생성자 호출시 생략될 수 있다.
initialize 함수는 전달된 coupon이 IborCoupon으로 downcasting에 의해 올바른 타입을 갖는지 확인한다. 만약 맞다면 몇가지 사전 처리가 수행된다. 여기서 두개의 데이터 멤버에서 coupon gearing과 spread를 저장한다; 이것은 나머지 코드를 더욱 간결하게 작성할수 있도록 도와준다. swapletRate함수는 간단히 저장된 gearing과 spread를 adjusted rate로 적용하고 이것의 계산은 adjustedFixing 함수로 위임된다. 만약 coupon이 tenor(투자, 대출 등의 계약기간 중 현재 시점에서의 잔여기간을 말합니다.)의 시작에서 고정되면, adjusted rate는 underlying index의 fixing이다. 만약 coupon이 연체된다면, convexity adjustment가 계산되고 fixing에 추가될 것이다; 여기서 공식에 대한 구현을 보이지는 않을 것이지만 유효한 caplet volatility를 위한 check를 할 것이다. capletRate와 같은 다른 함수는 여기서 다루지 않을 것이다; 나중에 예제로 다룰 것이다.
이것이 간단하지만 clean한 예제이다 - par coupons를 사용하여 floating-rate notes를 pricing하는 법. 라이브러리는 이러한 practice를 따르거나 그렇지 않도록 하게 할 수 있다; 이러한 방법은 몇가지가 있다.
첫번째로 (listing 4.8) pricer machinery를 bypass하고 LIBOR coupon을 위한 rate 함수를 override하는 것이다. 만약 par coupons가 compilation flag를 셋팅하는 것에 의해 사용할 수 있고 coupon이 연체되지 않는다면(이 경우 여전히 pricer를 필요로 한다), built-in par-rate 계산이 사용된다; 다시 말하면 함수 호출이 base 클래스 함수로 보내진다면 계산은 pricer로 위임된다. 이것은 동작하지만 단점을 가지고 있다.
// Listing 4.8: Extension of the IborCoupon class to support par coupons.
반면 pricer를 coupon에 설정하게 되면 observable 효과를 누릴수 없게 된다.
어떤 다른 index fixing은 같은 날짜상에서 연체되지 않은 coupons fixing이나 연체가 된 것을 위해 사용될 수 있다. 이것은 parity relationships or worse, hedges를 버릴수(throw off) 있다.
더 좋은 방법은 par-coupon pricer를 명시적으로 구현하는 것이다(Listing 4.9). 이것은 Black pricer를 상속함으로 인해 이루어진다.
// Listing 4.9: Sketch of the ParCouponPricer class.
파생 pricer는 오직 adjustedFixing 함수를 override하면 된다; fixing을 위한 저장된 index를 asking하는 것 대신에 risk-free curve를 extract하고 그것을 par rate를 계산하는데 사용한다. 이것은 coupon 클래스에서 rate를 override하는 것보다 cleaner하고 같은 단점을 겪지 않게 되며 런타임시 선택할 수 있다.
이 방법은 여전히 한가지 문제을 갖지만; convexityAdjustment 함수는 par fixing과 index fixing(convexity 효과때문이 아닌) 사이의 difference를 리턴할 것이다(coupon's rate 함수를 override할 때 같은 문제에 직면하게 된다). 불행히, 이것을 어떻게 고칠지는 분명하지 않다 - 최소한 나에게- 비록 몇개의 가능성은 있지만. 하나는 par coupons를 detect하고 null adjustment를 리턴하기 위해 convexityAdjustment를 override하는 것이지만 이것은 연체 coupons에는 동작하지 않을 것이다. 다른 가능성은 함수 이름을 adjustment로 바꾸고 더욱 일반화 시키는 것이지만 이것은 정보를 잃게된다. Best는 convexity adjustment의 계산을 pricer에 위임하는 것이다; 이것은 fragility 이슈를 극복하는데도 도움이 된다.
4.2.4 Example: capped/floored coupons
Caps와 floors는 어떤 종류의 floating-rate coupon에도 적용할 수 있다. 이 프레임워크에서 이것은 FloatingRateCoupon으로부터 파생되는 클래스에 generic way로 특징을 추가하기를 원하는 것을 의미한다.
이러한 요구사항은 Decorator pattern을 사용하기를 제안하게 된다. CappedFlorredCoupon 클래스의 인터페이스는 listing 4.10에 있고 구현은 4.11.에 있다.
// Listing 4.10: Interface of the CappedFlooredCoupon class.
클래스는 베이스 FloatingRateCoupon 클래스의 포인터를 저장하고 필요한 행동을 추가하며 다른 곳에 저장된 객체의 함수를 호출하면서 Decorator pattern의 표준(canonical)을 따른다. 그러나 다른 언어와는 다르게 C++은 Decorator pattern을 구현하는 다른 방법을 제공한다: 다음과 같이 templates와 상속을 이용한다.
template <class CouponType>
class CappedFloored : public CouponType ;
class template은 필요한 행동을 추가하거나 각각의 베이스 클래스의 함수를 호출하는 여러 클래스들(CappedFloored<IborCoupon> 또는 CappedFloored<CmsCoupon>과 같은)을 instantiate하는데 사용된다. 이때 기존의 것을 template 구현으로 대체할만한 강한 근거를 찾지 못했다; 그러나 CappedFlooredCoupon을 설명하는 동안 class template가 다르다(better or worse)는 것에 대해 살펴볼 것이다.
놀랍지 않게도, 생성자는 데이터 멤버에 FloatingRateCoupon 인스턴스를 decorate하고 저장하기 위해 취한다.
// Listing 4.11: Implementation of the CappedFlooredCoupon class.
'quantlib > Implementation' 카테고리의 다른 글
Aside: keeping one's balance (0) | 2011.06.28 |
---|---|
Aside: late payments (0) | 2011.06.27 |
Aside: Cinderella method (0) | 2011.06.27 |
Aside: a friend in need (0) | 2011.06.20 |
TTP: Template template parameter (0) | 2011.06.20 |