|
From: Luigi B. <lui...@gm...> - 2021-05-10 07:05:39
|
Hi Kenneth,
one thing you might want to check is the units for rates — when you say
1Y -> -0.12, i.e. -12 bps, that should be passed as -0.0012. (You might
already do that, but I thought I'd check.)
Luigi
On Sun, May 9, 2021 at 4:59 PM Kenneth Christensen <ke...@mo...>
wrote:
> Hi all!
>
> 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> ¬ionals,
> 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:
>
> ```1Y -> -0.12
> 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 ¬ional : 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](
> http://www.nasdaqomxnordic.com/bonds/denmark/microsite?Instrument=XCSE0%3A5RD27SSA53)
> 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?
>
> Thanks a lot for you help!
>
> Kenneth
>
>
>
> _______________________________________________
> QuantLib-users mailing list
> Qua...@li...
> https://lists.sourceforge.net/lists/listinfo/quantlib-users
>
|