1
$\begingroup$

I am trying to price a callable fixed rate amortizing danish mortgage bond. since QuantLib only has CallableFixedRateBond i have created a dervived class called CallableAmortizedBond which looks like AmortizingFixedRateBond:

class CallableAmortizedBond : public CallableFixedRateBond
{

public:
    CallableAmortizedBond(
        Natural settlementDays,
        Real faceAmount,
        const Schedule &schedule,
        const vector<Rate> &coupons,
        const DayCounter &accrualDayCounter,
        BusinessDayConvention paymentConvention,
        Real redemption,
        const Date &issueDate,
        const vector<Rate> &notionals,
        const CallabilitySchedule &putCallSchedule)
        : CallableFixedRateBond(settlementDays, faceAmount, schedule, coupons, accrualDayCounter, paymentConvention, redemption, issueDate, putCallSchedule)
    {
        cashflows_ =
            FixedRateLeg(schedule)
                .withNotionals(notionals)
                .withCouponRates(coupons, accrualDayCounter)
                .withPaymentAdjustment(paymentConvention);
    }
};

I have added notionals with inspiration from:

Schedule sinkingSchedule(const Date& startDate,
                            const Period& maturityTenor,
                            const Frequency& sinkingFrequency,
                            const Calendar& paymentCalendar)

Which can look like this for different bonds:

For bond with no amortization it will be a vector of faceValues e.g. [100.0 .. 100.0] For bonds with partial amortization it will be with faceValues until amortization starts and then fully amortizing: [100.0, 100.0 .. 99.0 97.0 .. 0.0] For bonds with full amortization it will be [100.0, 99.0 .. 0.0]

I use swaprates to create a YieldTermStructure with the following values:

2Y -> -0.13
3Y -> -0.09
4Y -> -0.03
5Y -> -0.03
6Y -> 0.1
7Y -> 0.18
8Y -> 0.25
9Y -> 0.32
10Y -> 0.39
12Y -> 0.51
15Y -> 0.65

like this:

Handle<YieldTermStructure> calculateTermStructure(rust::Vec<SwapRates> swapRates, RustDate now)
{
  Calendar calendar = TARGET();
  Date settlementDate(now.day, getMonthFromInt(now.month), now.year);
  Frequency swFixedLegFrequency = Annual;
  BusinessDayConvention swFixedLegConvention = Unadjusted;
  DayCounter swFixedLegDayCounter = dayCounter;
  DayCounter termStructureDayCounter = dayCounter;
  ext::shared_ptr<IborIndex> swFloatingLegIndex(new Euribor1Y);

  const Period forwardStart(1 * Days);

  std::vector<ext::shared_ptr<RateHelper>> swapInstruments;
  for (auto const &swapRate : swapRates)
  {
    ext::shared_ptr<Quote> simpleQuote(new SimpleQuote(swapRate.rate));
    ext::shared_ptr<RateHelper> swapRateHelper(new SwapRateHelper(
        Handle<Quote>(simpleQuote), swapRate.maturity * Years,
        calendar, swFixedLegFrequency,
        swFixedLegConvention, swFixedLegDayCounter,
        swFloatingLegIndex, Handle<Quote>(), forwardStart));
    swapInstruments.push_back(swapRateHelper);
  }

  ext::shared_ptr<YieldTermStructure> swapTermStructure(
      new PiecewiseYieldCurve<Discount, LogLinear>(
          settlementDate, swapInstruments,
          termStructureDayCounter));
  Handle<YieldTermStructure> termStructure(swapTermStructure);

  termStructure->enableExtrapolation();
  return termStructure;
}

I create the CallabilitySchedule using the following function:

CallabilitySchedule getCallSchedule(rust::Vec<RustDate> callSchedule)
{

  CallabilitySchedule callabilitySchedule;
  Real callPrice = 100.;

  for (auto const &callRustRate : callSchedule)
  {
    Bond::Price bondCallPrice(callPrice, Bond::Price::Clean);

    Date callDate(callRustRate.day, getMonthFromInt(callRustRate.month), callRustRate.year);
    callabilitySchedule.push_back(
        ext::make_shared<Callability>(
            bondCallPrice,
            Callability::Call,
            callDate));
  }

  return callabilitySchedule;
}

Which i then use to price a callable mortgage bond:

  Integer gridIntervals = 40;
  Real reversionParameter = .03;

  // output price/yield results for varying volatility parameter

  Real sigma = QL_EPSILON; // core dumps if zero on Cygwin

  ext::shared_ptr<ShortRateModel> hw0(
      new HullWhite(termStructure, reversionParameter, sigma));

  ext::shared_ptr<PricingEngine> engine0(
      new TreeCallableFixedRateBondEngine(hw0, gridIntervals));

  ext::shared_ptr<PricingEngine> bondEngine(
      new DiscountingBondEngine(termStructure));

  vector<Rate> notionalRates;
  for (auto const &notional : notionals)
  {
    notionalRates.push_back(notional);
  }

  CallableAmortizedBond callableAmortizedBond(settlementDays, faceAmount, schedule,
                                              vector<Rate>(1, coupon),
                                              dayCounter, paymentConvention,
                                              redemption, datedDate, notionalRates, callabilitySchedule);

  callableAmortizedBond.setPricingEngine(engine0);

  return callableAmortizedBond.cleanPrice();

The price returned using .cleanPrice() is much lower than the price found on Nasdaq. For example this bond has a last price of 91.210 but my price using the current swap rates is 88.542 with no amortization.

Another issue is that with amortization i get lower prices than without. Which is opposite of what happens in the real world. So is this correct way to price callable amortizing mortgage bonds? And is it the correct way to add amortization for the callable bond class?

$\endgroup$

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Browse other questions tagged or ask your own question.