Contents

QuantLib设计模式之:观察者模式

观察者模式(Observer/Observables Pattern)可以实现“一对多”的关系,一个对象的更新其依赖的对象也会随之更新,是Quantlib中最重要的模式之一。观察者模式在量化金融中应用的非常广泛,比如金融产品依赖于曲线定价,曲线的更新相对应的金融产品的价格需要重新计算,本文中分享观察者模式的应用。

Observer/Observable的接口

观察者模式分为:观察者(Observer)和被观察者(Observable)两个基类,其中观察者类中提供两接口函数:

  • registerWith()
  • unregisterWith()

两个接口函数入参都是被观察者对象或者指针,然后在函数中将自身注册给被观察对象,同时观察者中有一个update成员函数用来实现接受被观察者通知之后的业务逻辑。

在被观察者类中有一个notifyObservers成员函数用于通知Observer对象,在被观察者中提供的对外接口时调用notifyObservers成员函数来通知观察者,在Quanlib中外部的数据比如行情,利率等,这些数据的更新都可能需要重新计算金融产品的价格或者风险。

示例

在QuantLib中已有Observer/Observable的基类,只需要继承对应的基类即可实现观察者模式的功能。

示例一:收益率和折现因子

场景:定义收益率和折现因子对象,如果收益率改变了,对应的折现因子也需要更新。

步骤一:将收益率定义为Observable对象,SimpleYield继承Observable,同时定义设置收益率函数setYield,并且在此函数中调用通知Observer的函数notifyObservers。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
class SimpleYield : public Observable
{
private:
 Rate yield_;

public:
 SimpleYield(const Rate& yield) : yield_(yield)
 {
 }

 Rate getYield() const { return yield_; }

 void setYield(const Rate& yield)
 {
  yield_ = yield;
  // yield has changed, notify observers!
  notifyObservers();
 }
};

步骤二:将折现因子定义为Observer对象,SimpleDiscountFactor继承Observer,在构造函数中传入Observable对象SimpleYield的指针,同时调用registerWith函数注册Observable对象。

这里SimpleDiscountFactor还继承了Observable对象是为了后续场景准备,和示例一没有关系。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class SimpleDiscountFactor : public Observable, public Observer
{
private:
 DiscountFactor df_;
 Time mat_;
 boost::shared_ptr<SimpleYield> y_;

public:
 SimpleDiscountFactor(const boost::shared_ptr<SimpleYield>& y, const Time& mat)
  : y_(y), mat_(mat)
 {
  // register yield as an observable!
  registerWith(y_);
  df_ = std::exp(-y_->getYield() * mat_);
 }

 void update()
 {
  // something has changed, recalculate yield
  df_ = std::exp(-y_->getYield() * mat_);
  // Notify observers if this class itself is observed by others.
  notifyObservers();
 }

 Real getDiscount() const
 {
  return df_;
 }
};

在下面的测试用例中,可以通过接口setYield重新设置收益率,对应的折现因子即可实现更新。

1
2
3
4
5
6
7
8
9
void DesignPatterns::testingDesignPatterns2()
{
 boost::shared_ptr<SimpleYield> myYield(new SimpleYield(0.03));
 Time mat = 1.0;
 SimpleDiscountFactor myDf(myYield, mat);
 std::cout << " Discount before update :" << myDf.getDiscount() << std::endl;
 myYield->setYield(0.01);
 std::cout << " Discount after update :" << myDf.getDiscount() << std::endl;
}

本示例输出结果为:

1
2
Discount before update :0.970446
Discount after update :0.99005

示例二:折现因子和现金流

场景:现金流会随着折现因子的更新而更新,因此折现因子为被观察的对象,现金流为观察者,从这里可以看出对于折现因子即为收益率的观察者也是现金流对象的被观察者,同时继承了Observable和Observer两个基类,现金流类实现如下,在构造函数中传入被观察的对象折现因子。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class SimpleDiscountedCF : public Observer
{
private:
 boost::shared_ptr<SimpleDiscountFactor> df_;
 Real discountedUnit_;
public:
 SimpleDiscountedCF(const boost::shared_ptr<SimpleDiscountFactor>& df) : df_(df)
 {
  discountedUnit_ = df_->getDiscount();
  registerWith(df_);
 }

 void update()
 {
  // something has changed, recalculate discount factor
  discountedUnit_ = df_->getDiscount();
 }

 Real discountCashFlow(const Real& amount) const
 {
  return discountedUnit_ * amount;
 }
};

下面的测试用例通过改变收益率,对于的折现的现金流100也会随之改变:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
void DesignPatterns::testingDesignPatterns3()
{
 boost::shared_ptr<SimpleYield> myYield(new SimpleYield(0.03));
 Time mat = 1.0;
 // construct observer of yield curve
 boost::shared_ptr<SimpleDiscountFactor> myDf(
  new SimpleDiscountFactor(myYield, mat));
 // construct observer of discount factor
 SimpleDiscountedCF myCf(myDf);
 std::cout << " Cash Flow before update :" << myCf.discountCashFlow(100.0) << std::endl;
 myYield->setYield(0.01);
 std::cout << " Cash Flow after update :" << myCf.discountCashFlow(100.0) << std::endl;
}

其输出结果为:

1
2
 Cash Flow before update :97.0446
 Cash Flow after update :99.005

示例三:改变估值日期

在Quanlib中可以设置全局的估值日期(evaluation date),在提供的观察者模式的接口中,也可以在直接将估值日期作为被观察者,改变全局的估值日期,对应产品的现金流即可重新计算,重新定义折现因子的类如下,同时在构造函数中注册估值日期。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class SimpleDiscountFactor1 : public Observable, Observer
{
private:
 DiscountFactor df_;
 Date evalDate_, matDate_;
 boost::shared_ptr<SimpleYield> y_;
 DayCounter dc_;
public:
 SimpleDiscountFactor1(const boost::shared_ptr<SimpleYield>& y,
                       const Date& matDate, const DayCounter& dc)
  : y_(y), matDate_(matDate), dc_(dc)
 {
  // register yield as an observable!
  evalDate_ = Settings::instance().evaluationDate();
  registerWith(y_);
  registerWith(Settings::instance().evaluationDate());
  df_ = exp(-y_->getYield() * dc_.yearFraction(evalDate_, matDate_));
 }

 void update()
 {
  // something has changed, recalculate discount factor
  evalDate_ = Settings::instance().evaluationDate();
  df_ = exp(-y_->getYield() * dc_.yearFraction(evalDate_, matDate_));
  notifyObservers();
 }

 Real getDiscount() const
 {
  return df_;
 }
};

下面是示例的测试函数:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
void DesignPatterns::testingDesignPatterns2a()
{
 boost::shared_ptr < SimpleYield > myYield(new SimpleYield(0.03));
 Date mat = Date::todaysDate() + 12 * Months;
 DayCounter dc = ActualActual(ActualActual::ISDA);
 SimpleDiscountFactor1 myDf(myYield, mat, dc);
 std::cout << " Discount before yield update :" << myDf.getDiscount() << std::endl;
 myYield->setYield(0.01);
 std::cout << " Discount after yield update :" << myDf.getDiscount() << std::endl;
 Settings::instance().evaluationDate() = mat - 1 * Months;
 std::cout << " Discount after evaluation date update :" << myDf.getDiscount() << std::endl;
}

上述代码的输出结果为:

1
2
3
 Discount before yield update :0.970493
 Discount after yield update :0.990066
 Discount after evaluation date update :0.999178