NPV calculation in Python from a discounting curve

classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|

NPV calculation in Python from a discounting curve

asavoldi
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 ''
Reply | Threaded
Open this post in threaded view
|

Re: NPV calculation in Python from a discounting curve

Luigi Ballabio
The expression
    settlementDate + 2*QuantLib.Months
doesn't work as you expect in Python, since there's no real
enumeration in the language and QuantLib.Months is just an integer.
You need to use
    settlementDate + QuantLib.Period(2, QuantLib.Months)
instead.

Luigi

On Wed, Feb 12, 2014 at 10:37 AM, asavoldi <[hidden email]> wrote:

> 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
> # <[hidden email]>. 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 ''
>
>
>
> --
> View this message in context: http://quantlib.10058.n7.nabble.com/NPV-calculation-in-Python-from-a-discounting-curve-tp14960.html
> Sent from the quantlib-users mailing list archive at Nabble.com.
>
> ------------------------------------------------------------------------------
> Android apps run on BlackBerry 10
> Introducing the new BlackBerry 10.2.1 Runtime for Android apps.
> Now with support for Jelly Bean, Bluetooth, Mapview and more.
> Get your Android app in front of a whole new audience.  Start now.
> http://pubads.g.doubleclick.net/gampad/clk?id=124407151&iu=/4140/ostg.clktrk
> _______________________________________________
> QuantLib-users mailing list
> [hidden email]
> https://lists.sourceforge.net/lists/listinfo/quantlib-users



--
<https://implementingquantlib.blogspot.com>
<https://twitter.com/lballabio>

------------------------------------------------------------------------------
Android apps run on BlackBerry 10
Introducing the new BlackBerry 10.2.1 Runtime for Android apps.
Now with support for Jelly Bean, Bluetooth, Mapview and more.
Get your Android app in front of a whole new audience.  Start now.
http://pubads.g.doubleclick.net/gampad/clk?id=124407151&iu=/4140/ostg.clktrk
_______________________________________________
QuantLib-users mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/quantlib-users
Reply | Threaded
Open this post in threaded view
|

Re: NPV calculation in Python from a discounting curve

asavoldi
Thank you so much Luigi.