QuantLib OpenOffice/Excel YIELD / PRICE functions
Asked Answered
R

1

17

Can somebody provide an example of how to replicate the Excel/OpenOffice YIELD and PRICE functions using QuantLib?

I have a few examples but I don't quite understand all the setup yet. When I try to change some values I either get zeros out or some nonsensical values. Ideally I'd like to create the c++ equivalent to the YIELD/PRICE functions.

In my first step I don't need to replicate the defects in the Excel date modelling. I can wait until later to produce an exact duplicate. Though if you know how that is also great.


PRICE example, in OpenOffice:

PRICE("2008-02-15","2010-11-15",5%,7%,100,2,1) = 95.068419616675

My QuantLib code is capable of getting 95.066759 which is a bit off. At least I have the basic price function, I'd like to get an exact match for the results now.


I can't easily include all the wrapping code, but the essential code is as follows.

#include <ql/time/calendar.hpp>
#include <ql/time/daycounters/actualactual.hpp>
#include <ql/time/daycounters/actual365fixed.hpp>
#include <ql/time/schedule.hpp>
#include <ql/time/calendars/unitedstates.hpp>
#include <ql/time/calendars/nullcalendar.hpp>

#include <ql/settings.hpp>
#include <ql/handle.hpp>
#include <ql/termstructures/yield/flatforward.hpp>
#include <ql/instruments/bonds/fixedratebond.hpp>

#include <ql/pricingengines/bond/discountingbondengine.hpp>
#include <ql/utilities/dataformatters.hpp>

#include <iostream>
#include <iomanip>

#include "boost/date_time/gregorian/gregorian.hpp"
using namespace QuantLib;

Date convert_date( boost::gregorian::date const & date )
{
    unsigned mon = date.month();
    return Date( date.day(), Month(mon), date.year() );
}

shared_ptr<Bond> create_bond( boost::gregorian::date const & settlement_, boost::gregorian::date const & maturity_,
    double coupon_, double yield_, double redemption_, unsigned frequency_ )
{
    // date set up
    //Calendar calendar = UnitedStates(UnitedStates::GovernmentBond);
    Calendar calendar = NullCalendar(); //small improvement

    Date settlementDate( convert_date( settlement_ ) );
    // the settlement date must be a business day
    settlementDate = calendar.adjust(settlementDate);

    Integer fixingDays = 0; //1;
    Natural settlementDays = 0; //1

    Date evalDate = calendar.advance(settlementDate, -fixingDays, Days);
    // Evaluation date (TODO: What should this actually be?)
    Settings::instance().evaluationDate() = evalDate;

    // bond set up
    Real faceAmount = 100;
    Real redemption = redemption_;
    Date issueDate( 1, January, 2001); //NOTE: shouldn't be relevant for price/yield calculations
    Date maturity( convert_date( maturity_ ) );
    Real couponRate = coupon_;
    Real yield = yield_;

    //ActualActual dayCounter( ActualActual::Bond );
    ActualActual dayCounter;
    //Actual365Fixed dayCounter;

    RelinkableHandle<YieldTermStructure> discountingTermStructure;
    boost::shared_ptr<YieldTermStructure> flatTermStructure(
        new FlatForward(
            settlementDate,
            yield,
            dayCounter,
            Compounded,
            Frequency( frequency_ ) ));
    discountingTermStructure.linkTo(flatTermStructure);

    boost::shared_ptr<PricingEngine> bondEngine(
        new DiscountingBondEngine(discountingTermStructure));

    Schedule fixedBondSchedule(
        issueDate,
        maturity,
        Period( Frequency( frequency_ ) ),
        calendar,
        Unadjusted,
        Unadjusted,
        DateGeneration::Backward,
        false /*EOM*/); //strangely makes no difference in our calculations

    boost::shared_ptr<Bond> fixedRateBond( new FixedRateBond(
        settlementDays,
        faceAmount,
        fixedBondSchedule,
        std::vector<Rate>(1, couponRate),
        dayCounter,
        Unadjusted,
        redemption) );

    fixedRateBond->setPricingEngine(bondEngine);
    return fixedRateBond;
}

//OpenOffice: PRICE("2008-02-15","2010-11-15",5%,7%,100,2,1)
double bond_price( boost::gregorian::date const & settlement_, boost::gregorian::date const & maturity_,
    double coupon_, double yield_, double redemption_, unsigned frequency_ )
{
    shared_ptr<Bond> bond( create_bond( settlement_, maturity_, coupon_, yield_, redemption_, frequency_ ) );
    return bond->cleanPrice();
}

//OpenOffice: PRICE("2008-02-15","2010-11-15",5%,7%,100,2,1)
double bond_yield( boost::gregorian::date const & settlement_, boost::gregorian::date const & maturity_,
    double coupon_, double price_, double redemption_, unsigned frequency_ )
{
    shared_ptr<Bond> bond( create_bond( settlement_, maturity_, coupon_, 0, redemption_, frequency_ ) );
    ActualActual dayCounter;
    return bond->yield( price_, dayCounter, Compounded, Frequency(frequency_) );
}
Ruthenious answered 7/3, 2011 at 15:24 Comment(3)
this is indicative of a numerical instability or rounding issue with floating point types. try performing your calculations using a gmp multiprecision type. Also note that financial date counting has many variations, perhaps the oo version is different to the one available in quantlib.Asylum
The difference is high enough that I'm not sure it would be a floating point error. Yet it is low enough to not be a single day difference in the day counter. I've look through the OO code and it is /questionable/ at times, but it correlates exactly to Excel. I am more inclined to trust QuantLib here, but it would be really nice to have a config that produces the same results.Ruthenious
I got the same result as you, I have basically made excatly the same assumption that you made. It can be my lack of experiences with quantlib, however it seems strange that the price depends on the FlatForward constractor. I am not sure, it is the right way to model a flat yield curve. In my mind a flat yield curve should be a function rate(time) that return the same value for each time. So a unique value should be enough to univocally specified it.Lane
G
2

If you are interested in the implementation of the PRICE function in OpenOffice, you can see the code in the implementation of AnalysisAddIn::getPrice. This refers to the getPrice_ function in analysis helper.cxx. Maybe you find out what's happening there.

Note, that OpenGrok seems to be misconfigured here, so clicking on functions may not work. But I suppose you find all you need in the files in the directory /OOO340_m0/scaddins/source/analysis.

Gasket answered 28/9, 2011 at 21:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.