Thursday, May 4, 2017

programming - Calculating Discount Margin on a floating rate bond using QuantLib


Going off Luigi's hint on this answer: Setting up Schedule for an amortizing floater in QuantLib


I was able to cobble something together but I'm unable to verify if it's correct. TLDR: I was able to generate this cashflow and got a Discount Margin value of 121.42091071060361 for the price input of 90.00. Any insights is appreciated.


First, let's set up the bond: It's a LIBOR+1.77 (vectors come from a third party).



  • Issue Date: 03/06/1997

  • Maturity: 03/06/2037


  • Next payment: 03/05/2018

  • Face: 12000000

  • Floater: 1.77

  • Reference Index: LIBOR_1MO (vectors: https://pastebin.com/HLYWsyux)


Setting up the bond: I use InterpolatedForwardCurve to setup my floating rates vectors and feed it to ForwardSpreadedTermStructure in order to accept a spread value. The basic idea is that since SimpleQuote, your input spread, is an observable, all changes to it will trigger recalculations in your bond class. I use this input in order to find the spread at which price exactly equals the current price (the price is an input; going from price to get DM).


DMFinder Function: My DMFinder inherits ISolver1d accepts a price as input. The value(x) implementation accepts a Spread value and tries to solve for v where the the resulting price exactly matches the original price; i.e. the spread value where the price exactly results in a null price at the current DM. As advised by @LuigiBallabio I'm also keeping an instance of the Bond class inside the DMFinder class to call cleanPrice() as the SimpleQuote value changes.


private class DMFinder : ISolver1d
{
private readonly FloatingRateBond bond_;

private SimpleQuote spread_;
private double price_;

public DMFinder(FloatingRateBond bond, double price, SimpleQuote spread)
{
this.bond_ = bond;
this.price_ = price;
this.spread_ = spread;
}


public override double value(double v)
{
this.spread.setValue(v);
var solvedPrice = bond.cleanPrice();
return this.price - solvedPrice;
}
}

Getting the DM through Solver1D:


public static double DM(FloatingRateBond bond, double price, SimpleQuote spread, double accuracy = 1.0e-10, int maxIterations = 100, double guess = 0.05)

{
var solver = new FiniteDifferenceNewtonSafe();
solver.SetMaxEvaluations(maxIterations);

var objFunction = new DMFinder(bond, price, spread);
var dm = solver.Solve(objFunction, accuracy, guess, guess / 10.0);
return dm;
}

Full bond setup: Full code below.



var settleDate = new Date(15, Month.Jan, 2018);                         // Settle date
var settlementDays = 3; // Settle day number, usually T+3
var faceAmount = 12000000.00; // Current face
var issueDate = new Date(3, Month.Jun, 1997); // Issue Date
var maturity = new Date(3, Month.Jun, 2037); // Maturity Date

var thirty360 = new Thirty360(); // Day counter, using 30/360 convention.
var calendar = new UnitedStates(UnitedStates.Market.Settlement); // Using USA settlement calendar for holidays/weekends detection
settleDate = calendar.adjust(settleDate);


// Evaluation must be a business day
var today = calendar.advance(settleDate, -settlementDays, TimeUnit.Days);
Settings.setEvaluationDate(today); // Set business day to today

// An observable, holds the Spread argument
var spread = new SimpleQuote(0.0);

// Vectors are here: https://pastebin.com/HLYWsyux
var yieldCurve = new InterpolatedForwardCurve(VECTOR_DATES, VECTORS, thirty360, calendar, new Linear());
yieldCurve.enableExtrapolation();

var spreadedYieldCurve = new ForwardSpreadedTermStructure(new Handle(yieldCurve), new Handle(spread));
spreadedYieldCurve.enableExtrapolation();

var discountingTs = new Handle(spreadedYieldCurve);
var indexTs = new Handle(spreadedYieldCurve);

var index = new Libor("USD Libor", // Family name
new Period(Frequency.Monthly), // Frequency of of rates
2, // Settlement days
new USDCurrency(), // Base currency

calendar, // Calendar used
thirty360, // Day counting convention
indexTs); // Class containing vectors and dates, as well as interpolation

// Generates the payment schedule, always start at issue date
var schedule = new Schedule(issueDate,
maturity,
new Period(Frequency.Quarterly),
calendar,
BusinessDayConvention.ModifiedFollowing,

BusinessDayConvention.ModifiedFollowing,
DateGeneration.Rule.Forward,
false);

// Setup the bond, issued 06/03/1997, libor+177, matures 06/03/2037
// Next payment: 03/05/2018
var bond = new FloatingRateBond(settlementDays,
faceAmount,
schedule,
index,

thirty360,
schedule.businessDayConvention(),
0, // Fixing days
new List() { 1, 1.0 }, // LIBOR multiplier (for example, 0.80 * libor)
new List() { 1, 0.0177 }, // The spread, or "libor plus" => in this case, x is 0.0177
new List() { }, // Caps
new List() { }, // Floors
true, // Index fixing in arrears?
100, // Percent of redemption at maturity
issueDate); // When bond was issued


var bondEngine = new DiscountingBondEngine(discountingTs);
bond.setPricingEngine(bondEngine);

// Sets the coupon pricer to the bond
var pricer = new BlackIborCouponPricer();
var vol = 0.0;
var volatility = new ConstantOptionletVolatility(3,
calendar,
BusinessDayConvention.ModifiedFollowing,

vol,
new Thirty360());

pricer.setCapletVolatility(new Handle(volatility));
HelperFunctions.setCouponPricer(bond.cashflows(), pricer);

// now calculate DM
var dm = Cashflows.DM(bond, 90, spread) * 100;


No comments:

Post a Comment

technique - How credible is wikipedia?

I understand that this question relates more to wikipedia than it does writing but... If I was going to use wikipedia for a source for a res...