NPV calculation in Python from a discounting curve

Posted by asavoldi on
URL: http://quantlib.414.s1.nabble.com/NPV-calculation-in-Python-from-a-discounting-curve-tp14960.html

Hello everybody,

I'm trying to calculate the NPV of a couple of bonds, zero coupon and fixed income, using QuantLib in Python, via SWIG interface (QuantLib 1.3, SWIG 1.2). In C++, starting from the discounting curve which is embedded within the code snippet posted below, I obtain the correct values:

NPV (ZCB) = 99.2665
NPV (FRB) = 103.733

However, from Python side, I've got different results:

NPV (ZCB) = 96.45
NPV (FRB) = 100.79

Could you help me to understand the reason of this mis-behaviour?

Thanks in advance,
Antonio








#!/usr/bin/env python

# Copyright (C) 2008 Florent Grenier
# Copyright (C) 2010 Lluis Pujol Bajador
#
# This file is part of QuantLib, a free-software/open-source library
# for financial quantitative analysts and developers - http://quantlib.org/
#
# QuantLib is free software: you can redistribute it and/or modify it
# under the terms of the QuantLib license.  You should have received a
# copy of the license along with this program; if not, please email
# <quantlib-dev@lists.sf.net>. The license is also available online at
# <http://quantlib.org/license.shtml>.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.  See the license for more details.

#    This example shows how to set up a term structure and then price
#    some simple bonds. The last part is dedicated to peripherical
#    computations such as "Yield to Price" or "Price to Yield"

# this script should be the prototype which demonstrate how to
# use discounting curve for calculating the Net Present Value
# of a Bond (Fixed Rate, Zero Coupon, and Floating Rate)

# Test cases for Bonds pricing module
# Fixed rate bond
# Zero coupon bond
# Floating rate bond

from QuantLib import *

# global data
calendar = TARGET()
settlementDate = Date(01,January,2014)
settlementDate = calendar.adjust(settlementDate)
fixingDays = 0
settlementDays = 0
todaysDate = calendar.adjust(calendar.advance(settlementDate, -fixingDays, Days))
Settings.instance().evaluationDate = todaysDate
termStructureDayCounter =  ActualActual(ActualActual.ISDA)

print 'Today: '  + str(todaysDate)
print 'Settlement Date: ' + str(settlementDate)

# dates and rates vectors
dates = [
         settlementDate,
         settlementDate + 1*QuantLib.Weeks,
         settlementDate + 1*QuantLib.Months,
         settlementDate + 2*QuantLib.Months,
         settlementDate + 3*QuantLib.Months,
         
         settlementDate + 4*QuantLib.Months,
         settlementDate + 5*QuantLib.Months,
         settlementDate + 6*QuantLib.Months,
         settlementDate + 9*QuantLib.Months,
         settlementDate + 1*12*QuantLib.Months,
         
         settlementDate + 18*QuantLib.Months,
         settlementDate + 2*12*QuantLib.Months,
         settlementDate + 3*12*QuantLib.Months,
         settlementDate + 4*12*QuantLib.Months,
         settlementDate + 5*12*QuantLib.Months,
         
         settlementDate + 6*12*QuantLib.Months,
         settlementDate + 7*12*QuantLib.Months,
         settlementDate + 8*12*QuantLib.Months,
         settlementDate + 9*12*QuantLib.Months,
         settlementDate + 10*12*QuantLib.Months,
         
         settlementDate + 11*12*QuantLib.Months,
         settlementDate + 12*12*QuantLib.Months,
         settlementDate + 15*12*QuantLib.Months,
         settlementDate + 20*12*QuantLib.Months,
         settlementDate + 25*12*QuantLib.Months,
         settlementDate + 30*12*30]



rates = [
        0.002762549,
        0.002762549,
        0.002762549,
        0.002762549,
        0.002762549,
       
        0.00347872,        
        0.004126332,
        0.004714056,
        0.006191223,
        0.007362001,
       
        0.009215772,
        0.01082479,
        0.01404554,
        0.01738827,
        0.02061265,
       
        0.02351719,        
        0.02602376,
        0.02813818,
        0.0299055,
        0.03138197,
       
        0.0326213,        
        0.03366947,
        0.03600337,
        0.03835103,
        0.03976059,
        0.04070031
        ]


#######################################
#        BONDS TO BE PRICED           #
#######################################

# common data

faceAmount = 100;

# Discounting curve built up on the provided dates/rates
bondDiscountingTermStructure = ZeroCurve(dates, rates, termStructureDayCounter)
discountingTermStructure = RelinkableYieldTermStructureHandle()
bondEngine = DiscountingBondEngine(discountingTermStructure)

# zero coupon bond
zeroCouponBond = ZeroCouponBond(settlementDays,
                                UnitedStates(UnitedStates.GovernmentBond),
                                faceAmount,
                                Date(01,January,2015),
                                Following,
                                100,
                                Date(01,January,2005))

zeroCouponBond.setPricingEngine(bondEngine)


# fixed 4.5% US Treasury note
fixedBondSchedule = Schedule(Date(01, January, 2005),
                             Date(01, January, 2015),
                             Period(Annual),
                             UnitedStates(UnitedStates.GovernmentBond),
                             Unadjusted,
                             Unadjusted,
                             DateGeneration.Backward,
                             False)

fixedRateBond = FixedRateBond(settlementDays,
                              faceAmount,
                              fixedBondSchedule,
                              [0.045],
                              ActualActual(ActualActual.Bond),
                              ModifiedFollowing,
                              100.0,
                              Date(01, January, 2005))

fixedRateBond.setPricingEngine(bondEngine);


discountingTermStructure.linkTo(bondDiscountingTermStructure)

#############################
#       BOND PRICING        #
#############################

# write column headings
def formatPrice(p,digits=2):
    format = '%%.%df' % digits
    return format % p

def formatRate(r,digits=2):
    format = '%%.%df %%%%' % digits
    return format % (r*100)

def report(Info, Zc, Fix, Frn, format):
    if format== "Price":
        Zc = formatPrice(Zc)
        Fix = formatPrice(Fix)
        Frn = formatPrice(Frn)
    else:
        if Info.find("coupon")==-1:
            Zc  = formatRate(Zc)
        else:
            Zc  = "N/A"
        Fix = formatRate(Fix)
        Frn = formatRate(Frn)
       
    print '%19s' % Info + ' |' + \
          ' |'.join(['%10s' % y for y in [Zc, Fix, Frn] ])



headers = [ "ZC", "Fixed", "Floating" ]
print ''
print '%19s' % '' + ' |' + \
          ' |'.join(['%10s' % y for y in headers])
                     
separator = " | "
widths = [ 18, 10, 10, 10 ]
width = widths[0] + widths[1] + widths[2]  + widths[3] + widths[3];
rule = "-" * width
dblrule = "=" * width
tab = " " * 8

print  rule
report( "Net present value",
        zeroCouponBond.NPV(),
        fixedRateBond.NPV(),
        #floatingRateBond.NPV(),
        0,
        "Price")
print ''