Question: small discrepancy in bond.cleanPrice() versus calling BondFunctions.cleanPrice() ?? help!

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

Question: small discrepancy in bond.cleanPrice() versus calling BondFunctions.cleanPrice() ?? help!

Nick Pierce
Hi

I’m very new to Quantlib - about a week in (quite amazed so far!), but I have managed to get some bonds working in Python 2.7, using SWIG. QL v 1.9.

I have set up a bond and the schedule is correct. When I calculate the clean price by inputting a yield (using BondFunctions.cleanPrice(…pass in yield)), this is correct (and yield from clean price), accrued is also correct and duration is fine as yield is not far out.

However, when I attach a yield curve (flat forward) with the same yield, I get a fractionally different / price yield calc. Dates in the schedule are unadjusted, not modified following.

Results:

Clean Price Expected                     : 179.03
Clean Price BondFunctions.cleanPrice()   : 179.03
Clean Price bond.cleanPrice()            : 179.09
Difference                               : 0.0626431535518

Yield Expected                           : -0.0145461
Yield BondFunctions.BondYield()          : -0.0145461
Yield bond.bondYield()                   : -0.01455624
Difference                               : 1.01382016107e-05

Duration Expected                        : 34.52
Duration BondFunctions.duration()        : 34.52
Accrued Expected                         : 0.0483425414365
Accrued BondFunctions.accruedAmount()    : 0.0483425414365
Accrued bond.accruedAmount()             : 0.0483425414365
Program ended with exit code: 0

As you can see the calculated clean price from bond.cleanPrice() is 6 cents different (incorrect)..and it’s been driving me mad!! The yield is different from the 5th decimal place.

This is a real bond and the input numbers have been verified using a manual calc in excel, they match the officially published numbers from the UK debt management office (negative yield
 is the real yield for an inflation linked gilt, i.e. the quote of 179.03 is the market quote (clean) for the UKTI 52’s, before the index ratio is applied).

In this example the price is the closing price for 30th Nov and settlement date is 1st Dec, 2016.

If anyone can point out where I have gone wrong it would be much appreciated.

Code is below.

Many thanks 

Nick

import QuantLib as ql
import datetime as dt
calendar = ql.UnitedKingdom()
coupons=[0.0025]
exCouponPeriod=ql.Period(6,ql.Days)
calendarCoupon=ql.UnitedKingdom()
faceValue=100
businessConvention = ql.Unadjusted
businessConventionCoupon=ql.Unadjusted

today = ql.Date(30,11,2016)
evaluationDate = calendar.adjust(today)
issue_date = calendar.advance(evaluationDate,-1, ql.Years)
maturity_date = ql.Date(22,3,2052)

settlementDays=1
settlementDate=today+1
dayCount=ql.ActualActual(ql.ActualActual.ISMA)


ql.Settings.instance().setEvaluationDate(evaluationDate)

#input values to be checked
inputYield=-0.014546100
inputCleanPrice=179.03
inputDuration=34.52
inputAccrued=0.0483425414365


#create schedule
fixedSchedule = ql.Schedule(issue_date,maturity_date,ql.Period(ql.Semiannual),calendar,ql.Unadjusted,ql.Unadjusted,ql.DateGeneration.Backward,False)

#create bond
fixed_rate_bond = ql.FixedRateBond(settlementDays,faceValue,fixedSchedule,coupons,dayCount,businessConvention,100,issue_date,calendar,exCouponPeriod,calendarCoupon,businessConventionCoupon,False)

#curve
ts_curve=ql.FlatForward(evaluationDate, inputYield, dayCount, ql.Compounded, ql.Semiannual)

#curve handle
ts_handle = ql.YieldTermStructureHandle(ts_curve)

#create bond engine
bond_engine = ql.DiscountingBondEngine(ts_handle)#set bond engine

#set pricing engine
fixed_rate_bond.setPricingEngine(bond_engine)

#calculate yield from clean price
#calculations using BondFunctions
bf_gry=ql.BondFunctions.bondYield(fixed_rate_bond,inputCleanPrice,dayCount,ql.Compounded,ql.Semiannual,settlementDate)
bf_cp=ql.BondFunctions.cleanPrice(fixed_rate_bond,bf_gry,dayCount,ql.Compounded,ql.Semiannual,settlementDate)
bf_ai=ql.BondFunctions.accruedAmount(fixed_rate_bond,settlementDate)
bf_md=ql.BondFunctions.duration(fixed_rate_bond,bf_gry,dayCount,ql.Compounded,ql.Semiannual,ql.Duration.Modified,settlementDate)

#Calculations using bond
b_gry=fixed_rate_bond.bondYield(dayCount,ql.Compounded,ql.Semiannual)
b_cp=fixed_rate_bond.cleanPrice()
b_ai=fixed_rate_bond.accruedAmount()

print "Clean Price Expected                     :",inputCleanPrice
print "Clean Price BondFunctions.cleanPrice()   :",round(bf_cp,2)
print "Clean Price bond.cleanPrice()            :",round(b_cp,2)
print "Difference                               :",b_cp-bf_cp
print ""
print "Yield Expected                           :",round(inputYield,8)
print "Yield BondFunctions.BondYield()          :",round(bf_gry,8)
print "Yield bond.bondYield()                   :",round(b_gry,8)
print "Difference                               :",bf_gry-b_gry
print ""

print "Duration Expected                        :",inputDuration
print "Duration BondFunctions.duration()        :",round(bf_md,2)

print "Accrued Expected                         :", inputAccrued
print "Accrued BondFunctions.accruedAmount()    :", bf_ai
print "Accrued bond.accruedAmount()             :", b_ai

------------------------------------------------------------------------------
Developer Access Program for Intel Xeon Phi Processors
Access to Intel Xeon Phi processor-based developer platforms.
With one year of Intel Parallel Studio XE.
Training and support from Colfax.
Order your platform today.http://sdm.link/xeonphi
_______________________________________________
QuantLib-users mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/quantlib-users
Reply | Threaded
Open this post in threaded view
|

Re: Question: small discrepancy in bond.cleanPrice() versus calling BondFunctions.cleanPrice() ?? help!

Peter Caspers-4
Hi Nick,

I am not sure but it might have to do with the way your flat forward
yield term structure and the yield-based bond functions measure the
time between the settlement date and the cashflow payment dates
effectively for the calculation of forward discount factors. The
former does

dayCounter(evalDate, cashflowDate) - dayCounter(evalDate, settlementDate)

whereas the latter

dayCounter(settlementDate, cashflowDate)

which might be different in your case for one or several cashflows. I
don't know the exact market convention for this but apparently the
latter is matching in your case and the former is slightly wrong.

You could check by replacing the day counter with another one for
which the two expressions above are identical for all cashflow dates
(of course the results get "wrong" then, but should be identical for
the different methods then).

Kind Regards
Peter


On 6 December 2016 at 19:39, Nick Pierce <[hidden email]> wrote:

> Hi
>
> I’m very new to Quantlib - about a week in (quite amazed so far!), but I
> have managed to get some bonds working in Python 2.7, using SWIG. QL v 1.9.
>
> I have set up a bond and the schedule is correct. When I calculate the clean
> price by inputting a yield (using BondFunctions.cleanPrice(…pass in yield)),
> this is correct (and yield from clean price), accrued is also correct and
> duration is fine as yield is not far out.
>
> However, when I attach a yield curve (flat forward) with the same yield, I
> get a fractionally different / price yield calc. Dates in the schedule are
> unadjusted, not modified following.
>
> Results:
>
> Clean Price Expected                     : 179.03
> Clean Price BondFunctions.cleanPrice()   : 179.03
> Clean Price bond.cleanPrice()            : 179.09
> Difference                               : 0.0626431535518
>
> Yield Expected                           : -0.0145461
> Yield BondFunctions.BondYield()          : -0.0145461
> Yield bond.bondYield()                   : -0.01455624
> Difference                               : 1.01382016107e-05
>
> Duration Expected                        : 34.52
> Duration BondFunctions.duration()        : 34.52
> Accrued Expected                         : 0.0483425414365
> Accrued BondFunctions.accruedAmount()    : 0.0483425414365
> Accrued bond.accruedAmount()             : 0.0483425414365
> Program ended with exit code: 0
>
> As you can see the calculated clean price from bond.cleanPrice() is 6 cents
> different (incorrect)..and it’s been driving me mad!! The yield is different
> from the 5th decimal place.
>
> This is a real bond and the input numbers have been verified using a manual
> calc in excel, they match the officially published numbers from the UK debt
> management office (negative yield
>  is the real yield for an inflation linked gilt, i.e. the quote of 179.03 is
> the market quote (clean) for the UKTI 52’s, before the index ratio is
> applied).
>
> In this example the price is the closing price for 30th Nov and settlement
> date is 1st Dec, 2016.
>
> If anyone can point out where I have gone wrong it would be much
> appreciated.
>
> Code is below.
>
> Many thanks
>
> Nick
>
> import QuantLib as ql
> import datetime as dt
> calendar = ql.UnitedKingdom()
> coupons=[0.0025]
> exCouponPeriod=ql.Period(6,ql.Days)
> calendarCoupon=ql.UnitedKingdom()
> faceValue=100
> businessConvention = ql.Unadjusted
> businessConventionCoupon=ql.Unadjusted
>
> today = ql.Date(30,11,2016)
> evaluationDate = calendar.adjust(today)
> issue_date = calendar.advance(evaluationDate,-1, ql.Years)
> maturity_date = ql.Date(22,3,2052)
>
> settlementDays=1
> settlementDate=today+1
> dayCount=ql.ActualActual(ql.ActualActual.ISMA)
>
>
> ql.Settings.instance().setEvaluationDate(evaluationDate)
>
> #input values to be checked
> inputYield=-0.014546100
> inputCleanPrice=179.03
> inputDuration=34.52
> inputAccrued=0.0483425414365
>
>
> #create schedule
> fixedSchedule =
> ql.Schedule(issue_date,maturity_date,ql.Period(ql.Semiannual),calendar,ql.Unadjusted,ql.Unadjusted,ql.DateGeneration.Backward,False)
>
> #create bond
> fixed_rate_bond =
> ql.FixedRateBond(settlementDays,faceValue,fixedSchedule,coupons,dayCount,businessConvention,100,issue_date,calendar,exCouponPeriod,calendarCoupon,businessConventionCoupon,False)
>
> #curve
> ts_curve=ql.FlatForward(evaluationDate, inputYield, dayCount, ql.Compounded,
> ql.Semiannual)
>
> #curve handle
> ts_handle = ql.YieldTermStructureHandle(ts_curve)
>
> #create bond engine
> bond_engine = ql.DiscountingBondEngine(ts_handle)#set bond engine
>
> #set pricing engine
> fixed_rate_bond.setPricingEngine(bond_engine)
>
> #calculate yield from clean price
> #calculations using BondFunctions
> bf_gry=ql.BondFunctions.bondYield(fixed_rate_bond,inputCleanPrice,dayCount,ql.Compounded,ql.Semiannual,settlementDate)
> bf_cp=ql.BondFunctions.cleanPrice(fixed_rate_bond,bf_gry,dayCount,ql.Compounded,ql.Semiannual,settlementDate)
> bf_ai=ql.BondFunctions.accruedAmount(fixed_rate_bond,settlementDate)
> bf_md=ql.BondFunctions.duration(fixed_rate_bond,bf_gry,dayCount,ql.Compounded,ql.Semiannual,ql.Duration.Modified,settlementDate)
>
> #Calculations using bond
> b_gry=fixed_rate_bond.bondYield(dayCount,ql.Compounded,ql.Semiannual)
> b_cp=fixed_rate_bond.cleanPrice()
> b_ai=fixed_rate_bond.accruedAmount()
>
> print "Clean Price Expected                     :",inputCleanPrice
> print "Clean Price BondFunctions.cleanPrice()   :",round(bf_cp,2)
> print "Clean Price bond.cleanPrice()            :",round(b_cp,2)
> print "Difference                               :",b_cp-bf_cp
> print ""
> print "Yield Expected                           :",round(inputYield,8)
> print "Yield BondFunctions.BondYield()          :",round(bf_gry,8)
> print "Yield bond.bondYield()                   :",round(b_gry,8)
> print "Difference                               :",bf_gry-b_gry
> print ""
>
> print "Duration Expected                        :",inputDuration
> print "Duration BondFunctions.duration()        :",round(bf_md,2)
>
> print "Accrued Expected                         :", inputAccrued
> print "Accrued BondFunctions.accruedAmount()    :", bf_ai
> print "Accrued bond.accruedAmount()             :", b_ai
>
> ------------------------------------------------------------------------------
> Developer Access Program for Intel Xeon Phi Processors
> Access to Intel Xeon Phi processor-based developer platforms.
> With one year of Intel Parallel Studio XE.
> Training and support from Colfax.
> Order your platform today.http://sdm.link/xeonphi
> _______________________________________________
> QuantLib-users mailing list
> [hidden email]
> https://lists.sourceforge.net/lists/listinfo/quantlib-users
>

------------------------------------------------------------------------------
Developer Access Program for Intel Xeon Phi Processors
Access to Intel Xeon Phi processor-based developer platforms.
With one year of Intel Parallel Studio XE.
Training and support from Colfax.
Order your platform today.http://sdm.link/xeonphi
_______________________________________________
QuantLib-users mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/quantlib-users
Reply | Threaded
Open this post in threaded view
|

Re: Question: small discrepancy in bond.cleanPrice() versus calling BondFunctions.cleanPrice() ?? help!

Nick Pierce
Hi Peter

Thanks for the quick response, much appreciated.

Yes I think you are correct…

using

dayCount=ql.ActualActual(ql.ActualActual.ISDA)
Clean Price Expected                     : 167.05
Clean Price BondFunctions.cleanPrice()   : 167.05
Clean Price bond.cleanPrice()            : 167.06
Difference                               : 0.00866617031875

Yield Expected                           : 0.01835696
Yield BondFunctions.BondYield()          : 0.01836077
Yield bond.bondYield()                   : 0.01835696
Difference                               : 3.81175596394e-06

using

dayCount=ql.Thirty360()
Clean Price Expected                     : 167.05
Clean Price BondFunctions.cleanPrice()   : 167.05
Clean Price bond.cleanPrice()            : 167.06
Difference                               : 0.00935696611168

Yield Expected                           : 0.01835696
Yield BondFunctions.BondYield()          : 0.01836097
Yield bond.bondYield()                   : 0.01835696
Difference                               : 4.01397533715e-06

So in short the BondFunctions call is unaffected by the change in dayCounter and correct in all cases.

However, the bond.cleanPrice() changes with the change in dayCounter and is ironically closer (less than 1 cent out) versus the original 6 cents.

..so my next daft question is…

Is there anyway to fix this and should they be consistent??? 

Thanks again.

Regards

Nick



On 6 Dec 2016, at 19:55, Peter Caspers <[hidden email]> wrote:

Hi Nick,

I am not sure but it might have to do with the way your flat forward
yield term structure and the yield-based bond functions measure the
time between the settlement date and the cashflow payment dates
effectively for the calculation of forward discount factors. The
former does

dayCounter(evalDate, cashflowDate) - dayCounter(evalDate, settlementDate)

whereas the latter

dayCounter(settlementDate, cashflowDate)

which might be different in your case for one or several cashflows. I
don't know the exact market convention for this but apparently the
latter is matching in your case and the former is slightly wrong.

You could check by replacing the day counter with another one for
which the two expressions above are identical for all cashflow dates
(of course the results get "wrong" then, but should be identical for
the different methods then).

Kind Regards
Peter


On 6 December 2016 at 19:39, Nick Pierce <[hidden email]> wrote:
Hi

I’m very new to Quantlib - about a week in (quite amazed so far!), but I
have managed to get some bonds working in Python 2.7, using SWIG. QL v 1.9.

I have set up a bond and the schedule is correct. When I calculate the clean
price by inputting a yield (using BondFunctions.cleanPrice(…pass in yield)),
this is correct (and yield from clean price), accrued is also correct and
duration is fine as yield is not far out.

However, when I attach a yield curve (flat forward) with the same yield, I
get a fractionally different / price yield calc. Dates in the schedule are
unadjusted, not modified following.

Results:

Clean Price Expected                     : 179.03
Clean Price BondFunctions.cleanPrice()   : 179.03
Clean Price bond.cleanPrice()            : 179.09
Difference                               : 0.0626431535518

Yield Expected                           : -0.0145461
Yield BondFunctions.BondYield()          : -0.0145461
Yield bond.bondYield()                   : -0.01455624
Difference                               : 1.01382016107e-05

Duration Expected                        : 34.52
Duration BondFunctions.duration()        : 34.52
Accrued Expected                         : 0.0483425414365
Accrued BondFunctions.accruedAmount()    : 0.0483425414365
Accrued bond.accruedAmount()             : 0.0483425414365
Program ended with exit code: 0

As you can see the calculated clean price from bond.cleanPrice() is 6 cents
different (incorrect)..and it’s been driving me mad!! The yield is different
from the 5th decimal place.

This is a real bond and the input numbers have been verified using a manual
calc in excel, they match the officially published numbers from the UK debt
management office (negative yield
is the real yield for an inflation linked gilt, i.e. the quote of 179.03 is
the market quote (clean) for the UKTI 52’s, before the index ratio is
applied).

In this example the price is the closing price for 30th Nov and settlement
date is 1st Dec, 2016.

If anyone can point out where I have gone wrong it would be much
appreciated.

Code is below.

Many thanks

Nick

import QuantLib as ql
import datetime as dt
calendar = ql.UnitedKingdom()
coupons=[0.0025]
exCouponPeriod=ql.Period(6,ql.Days)
calendarCoupon=ql.UnitedKingdom()
faceValue=100
businessConvention = ql.Unadjusted
businessConventionCoupon=ql.Unadjusted

today = ql.Date(30,11,2016)
evaluationDate = calendar.adjust(today)
issue_date = calendar.advance(evaluationDate,-1, ql.Years)
maturity_date = ql.Date(22,3,2052)

settlementDays=1
settlementDate=today+1
dayCount=ql.ActualActual(ql.ActualActual.ISMA)


ql.Settings.instance().setEvaluationDate(evaluationDate)

#input values to be checked
inputYield=-0.014546100
inputCleanPrice=179.03
inputDuration=34.52
inputAccrued=0.0483425414365


#create schedule
fixedSchedule =
ql.Schedule(issue_date,maturity_date,ql.Period(ql.Semiannual),calendar,ql.Unadjusted,ql.Unadjusted,ql.DateGeneration.Backward,False)

#create bond
fixed_rate_bond =
ql.FixedRateBond(settlementDays,faceValue,fixedSchedule,coupons,dayCount,businessConvention,100,issue_date,calendar,exCouponPeriod,calendarCoupon,businessConventionCoupon,False)

#curve
ts_curve=ql.FlatForward(evaluationDate, inputYield, dayCount, ql.Compounded,
ql.Semiannual)

#curve handle
ts_handle = ql.YieldTermStructureHandle(ts_curve)

#create bond engine
bond_engine = ql.DiscountingBondEngine(ts_handle)#set bond engine

#set pricing engine
fixed_rate_bond.setPricingEngine(bond_engine)

#calculate yield from clean price
#calculations using BondFunctions
bf_gry=ql.BondFunctions.bondYield(fixed_rate_bond,inputCleanPrice,dayCount,ql.Compounded,ql.Semiannual,settlementDate)
bf_cp=ql.BondFunctions.cleanPrice(fixed_rate_bond,bf_gry,dayCount,ql.Compounded,ql.Semiannual,settlementDate)
bf_ai=ql.BondFunctions.accruedAmount(fixed_rate_bond,settlementDate)
bf_md=ql.BondFunctions.duration(fixed_rate_bond,bf_gry,dayCount,ql.Compounded,ql.Semiannual,ql.Duration.Modified,settlementDate)

#Calculations using bond
b_gry=fixed_rate_bond.bondYield(dayCount,ql.Compounded,ql.Semiannual)
b_cp=fixed_rate_bond.cleanPrice()
b_ai=fixed_rate_bond.accruedAmount()

print "Clean Price Expected                     :",inputCleanPrice
print "Clean Price BondFunctions.cleanPrice()   :",round(bf_cp,2)
print "Clean Price bond.cleanPrice()            :",round(b_cp,2)
print "Difference                               :",b_cp-bf_cp
print ""
print "Yield Expected                           :",round(inputYield,8)
print "Yield BondFunctions.BondYield()          :",round(bf_gry,8)
print "Yield bond.bondYield()                   :",round(b_gry,8)
print "Difference                               :",bf_gry-b_gry
print ""

print "Duration Expected                        :",inputDuration
print "Duration BondFunctions.duration()        :",round(bf_md,2)

print "Accrued Expected                         :", inputAccrued
print "Accrued BondFunctions.accruedAmount()    :", bf_ai
print "Accrued bond.accruedAmount()             :", b_ai

------------------------------------------------------------------------------
Developer Access Program for Intel Xeon Phi Processors
Access to Intel Xeon Phi processor-based developer platforms.
With one year of Intel Parallel Studio XE.
Training and support from Colfax.
Order your platform today.http://sdm.link/xeonphi
_______________________________________________
QuantLib-users mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/quantlib-users



------------------------------------------------------------------------------
Developer Access Program for Intel Xeon Phi Processors
Access to Intel Xeon Phi processor-based developer platforms.
With one year of Intel Parallel Studio XE.
Training and support from Colfax.
Order your platform today.http://sdm.link/xeonphi
_______________________________________________
QuantLib-users mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/quantlib-users
Reply | Threaded
Open this post in threaded view
|

Re: Question: small discrepancy in bond.cleanPrice() versus calling BondFunctions.cleanPrice() ?? help!

Ferdinando Ametrano-4
I cannot test it right now, but the issue might be solved if instead of
ts_curve=ql.FlatForward(evaluationDate, inputYield, dayCount, ql.Compounded, ql.Semiannual)

you would use
ts_curve=ql.FlatForward(settlementDate, inputYield, dayCount, ql.Compounded, ql.Semiannual)

as the latter is the proper way to set the time origin in your case


On Tue, Dec 6, 2016 at 9:17 PM, Nick Pierce <[hidden email]> wrote:
Hi Peter

Thanks for the quick response, much appreciated.

Yes I think you are correct…

using

dayCount=ql.ActualActual(ql.ActualActual.ISDA)
Clean Price Expected                     : 167.05
Clean Price BondFunctions.cleanPrice()   : 167.05
Clean Price bond.cleanPrice()            : 167.06
Difference                               : 0.00866617031875

Yield Expected                           : 0.01835696
Yield BondFunctions.BondYield()          : 0.01836077
Yield bond.bondYield()                   : 0.01835696
Difference                               : 3.81175596394e-06

using

dayCount=ql.Thirty360()
Clean Price Expected                     : 167.05
Clean Price BondFunctions.cleanPrice()   : 167.05
Clean Price bond.cleanPrice()            : 167.06
Difference                               : 0.00935696611168

Yield Expected                           : 0.01835696
Yield BondFunctions.BondYield()          : 0.01836097
Yield bond.bondYield()                   : 0.01835696
Difference                               : 4.01397533715e-06

So in short the BondFunctions call is unaffected by the change in dayCounter and correct in all cases.

However, the bond.cleanPrice() changes with the change in dayCounter and is ironically closer (less than 1 cent out) versus the original 6 cents.

..so my next daft question is…

Is there anyway to fix this and should they be consistent??? 

Thanks again.

Regards

Nick



On 6 Dec 2016, at 19:55, Peter Caspers <[hidden email]> wrote:

Hi Nick,

I am not sure but it might have to do with the way your flat forward
yield term structure and the yield-based bond functions measure the
time between the settlement date and the cashflow payment dates
effectively for the calculation of forward discount factors. The
former does

dayCounter(evalDate, cashflowDate) - dayCounter(evalDate, settlementDate)

whereas the latter

dayCounter(settlementDate, cashflowDate)

which might be different in your case for one or several cashflows. I
don't know the exact market convention for this but apparently the
latter is matching in your case and the former is slightly wrong.

You could check by replacing the day counter with another one for
which the two expressions above are identical for all cashflow dates
(of course the results get "wrong" then, but should be identical for
the different methods then).

Kind Regards
Peter


On 6 December 2016 at 19:39, Nick Pierce <[hidden email]> wrote:
Hi

I’m very new to Quantlib - about a week in (quite amazed so far!), but I
have managed to get some bonds working in Python 2.7, using SWIG. QL v 1.9.

I have set up a bond and the schedule is correct. When I calculate the clean
price by inputting a yield (using BondFunctions.cleanPrice(…pass in yield)),
this is correct (and yield from clean price), accrued is also correct and
duration is fine as yield is not far out.

However, when I attach a yield curve (flat forward) with the same yield, I
get a fractionally different / price yield calc. Dates in the schedule are
unadjusted, not modified following.

Results:

Clean Price Expected                     : 179.03
Clean Price BondFunctions.cleanPrice()   : 179.03
Clean Price bond.cleanPrice()            : 179.09
Difference                               : 0.0626431535518

Yield Expected                           : -0.0145461
Yield BondFunctions.BondYield()          : -0.0145461
Yield bond.bondYield()                   : -0.01455624
Difference                               : 1.01382016107e-05

Duration Expected                        : 34.52
Duration BondFunctions.duration()        : 34.52
Accrued Expected                         : 0.0483425414365
Accrued BondFunctions.accruedAmount()    : 0.0483425414365
Accrued bond.accruedAmount()             : 0.0483425414365
Program ended with exit code: 0

As you can see the calculated clean price from bond.cleanPrice() is 6 cents
different (incorrect)..and it’s been driving me mad!! The yield is different
from the 5th decimal place.

This is a real bond and the input numbers have been verified using a manual
calc in excel, they match the officially published numbers from the UK debt
management office (negative yield
is the real yield for an inflation linked gilt, i.e. the quote of 179.03 is
the market quote (clean) for the UKTI 52’s, before the index ratio is
applied).

In this example the price is the closing price for 30th Nov and settlement
date is 1st Dec, 2016.

If anyone can point out where I have gone wrong it would be much
appreciated.

Code is below.

Many thanks

Nick

import QuantLib as ql
import datetime as dt
calendar = ql.UnitedKingdom()
coupons=[0.0025]
exCouponPeriod=ql.Period(6,ql.Days)
calendarCoupon=ql.UnitedKingdom()
faceValue=100
businessConvention = ql.Unadjusted
businessConventionCoupon=ql.Unadjusted

today = ql.Date(30,11,2016)
evaluationDate = calendar.adjust(today)
issue_date = calendar.advance(evaluationDate,-1, ql.Years)
maturity_date = ql.Date(22,3,2052)

settlementDays=1
settlementDate=today+1
dayCount=ql.ActualActual(ql.ActualActual.ISMA)


ql.Settings.instance().setEvaluationDate(evaluationDate)

#input values to be checked
inputYield=-0.014546100
inputCleanPrice=179.03
inputDuration=34.52
inputAccrued=0.0483425414365


#create schedule
fixedSchedule =
ql.Schedule(issue_date,maturity_date,ql.Period(ql.Semiannual),calendar,ql.Unadjusted,ql.Unadjusted,ql.DateGeneration.Backward,False)

#create bond
fixed_rate_bond =
ql.FixedRateBond(settlementDays,faceValue,fixedSchedule,coupons,dayCount,businessConvention,100,issue_date,calendar,exCouponPeriod,calendarCoupon,businessConventionCoupon,False)

#curve
ts_curve=ql.FlatForward(evaluationDate, inputYield, dayCount, ql.Compounded,
ql.Semiannual)

#curve handle
ts_handle = ql.YieldTermStructureHandle(ts_curve)

#create bond engine
bond_engine = ql.DiscountingBondEngine(ts_handle)#set bond engine

#set pricing engine
fixed_rate_bond.setPricingEngine(bond_engine)

#calculate yield from clean price
#calculations using BondFunctions
bf_gry=ql.BondFunctions.bondYield(fixed_rate_bond,inputCleanPrice,dayCount,ql.Compounded,ql.Semiannual,settlementDate)
bf_cp=ql.BondFunctions.cleanPrice(fixed_rate_bond,bf_gry,dayCount,ql.Compounded,ql.Semiannual,settlementDate)
bf_ai=ql.BondFunctions.accruedAmount(fixed_rate_bond,settlementDate)
bf_md=ql.BondFunctions.duration(fixed_rate_bond,bf_gry,dayCount,ql.Compounded,ql.Semiannual,ql.Duration.Modified,settlementDate)

#Calculations using bond
b_gry=fixed_rate_bond.bondYield(dayCount,ql.Compounded,ql.Semiannual)
b_cp=fixed_rate_bond.cleanPrice()
b_ai=fixed_rate_bond.accruedAmount()

print "Clean Price Expected                     :",inputCleanPrice
print "Clean Price BondFunctions.cleanPrice()   :",round(bf_cp,2)
print "Clean Price bond.cleanPrice()            :",round(b_cp,2)
print "Difference                               :",b_cp-bf_cp
print ""
print "Yield Expected                           :",round(inputYield,8)
print "Yield BondFunctions.BondYield()          :",round(bf_gry,8)
print "Yield bond.bondYield()                   :",round(b_gry,8)
print "Difference                               :",bf_gry-b_gry
print ""

print "Duration Expected                        :",inputDuration
print "Duration BondFunctions.duration()        :",round(bf_md,2)

print "Accrued Expected                         :", inputAccrued
print "Accrued BondFunctions.accruedAmount()    :", bf_ai
print "Accrued bond.accruedAmount()             :", b_ai

------------------------------------------------------------------------------
Developer Access Program for Intel Xeon Phi Processors
Access to Intel Xeon Phi processor-based developer platforms.
With one year of Intel Parallel Studio XE.
Training and support from Colfax.
Order your platform today.http://sdm.link/xeonphi
_______________________________________________
QuantLib-users mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/quantlib-users



------------------------------------------------------------------------------
Developer Access Program for Intel Xeon Phi Processors
Access to Intel Xeon Phi processor-based developer platforms.
With one year of Intel Parallel Studio XE.
Training and support from Colfax.
Order your platform today.http://sdm.link/xeonphi
_______________________________________________
QuantLib-users mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/quantlib-users



------------------------------------------------------------------------------
Developer Access Program for Intel Xeon Phi Processors
Access to Intel Xeon Phi processor-based developer platforms.
With one year of Intel Parallel Studio XE.
Training and support from Colfax.
Order your platform today.http://sdm.link/xeonphi
_______________________________________________
QuantLib-users mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/quantlib-users
Reply | Threaded
Open this post in threaded view
|

Question: small discrepancy in bond.cleanPrice() versus calling BondFunctions.cleanPrice() ?? Update - only ActualActual.ISMA is different, all other day counts OK.

Nick Pierce
Thanks Ferdinando

I made the change as you suggested, but alas it didn’t solve the problem, so I took a different approach and ran the same bond set up with a number of day counts in the curve and bond, as opposed to just the curve (using settlementDate).

The results are below (different bond, but generically the same problem):

The bond.cleanPrice() and BondFunctions.cleanPrice() return the same value in every case except the ActualAcual.ISMA.

I also changed the schedule generation and bond set up to be modified following instead of unadjusted, just in case it was a weekend problem, but it made no difference.

Does this suggest there is a bug in the ISMA generation of a curve??

Any further help much appreciated.

Thanks

Nick

The result we are expecting is 179.03 (round to 2 d.p), using ISMA.

Actual/Actual (ISMA) day counter
bond.cleanPrice()=           179.099809062 <<<<this is the wrong one
BondFunctions.cleanPrice()=  179.029981553 <<<<correct when rounded to 2 decimal places
Actual/Actual (ISDA) day counter
bond.cleanPrice()=           179.028406531
BondFunctions.cleanPrice()=  179.028406531
Actual/365 (Fixed) day counter
bond.cleanPrice()=           179.086145534
BondFunctions.cleanPrice()=  179.086145534
Actual/365 (NL) day counter
bond.cleanPrice()=           179.023736844
BondFunctions.cleanPrice()=  179.023736844
30/360 (Bond Basis) day counter
bond.cleanPrice()=           179.034435254
BondFunctions.cleanPrice()=  179.034435254

On 9 Dec 2016, at 07:20, Ferdinando M. Ametrano <[hidden email]> wrote:

I cannot test it right now, but the issue might be solved if instead of
ts_curve=ql.FlatForward(evaluationDate, inputYield, dayCount, ql.Compounded, ql.Semiannual)

you would use
ts_curve=ql.FlatForward(settlementDate, inputYield, dayCount, ql.Compounded, ql.Semiannual)

as the latter is the proper way to set the time origin in your case


On Tue, Dec 6, 2016 at 9:17 PM, Nick Pierce <[hidden email]> wrote:
Hi Peter

Thanks for the quick response, much appreciated.

Yes I think you are correct…

using

dayCount=ql.ActualActual(ql.ActualActual.ISDA)
Clean Price Expected                     : 167.05
Clean Price BondFunctions.cleanPrice()   : 167.05
Clean Price bond.cleanPrice()            : 167.06
Difference                               : 0.00866617031875

Yield Expected                           : 0.01835696
Yield BondFunctions.BondYield()          : 0.01836077
Yield bond.bondYield()                   : 0.01835696
Difference                               : 3.81175596394e-06

using

dayCount=ql.Thirty360()
Clean Price Expected                     : 167.05
Clean Price BondFunctions.cleanPrice()   : 167.05
Clean Price bond.cleanPrice()            : 167.06
Difference                               : 0.00935696611168

Yield Expected                           : 0.01835696
Yield BondFunctions.BondYield()          : 0.01836097
Yield bond.bondYield()                   : 0.01835696
Difference                               : 4.01397533715e-06

So in short the BondFunctions call is unaffected by the change in dayCounter and correct in all cases.

However, the bond.cleanPrice() changes with the change in dayCounter and is ironically closer (less than 1 cent out) versus the original 6 cents.

..so my next daft question is…

Is there anyway to fix this and should they be consistent??? 

Thanks again.

Regards

Nick



On 6 Dec 2016, at 19:55, Peter Caspers <[hidden email]> wrote:

Hi Nick,

I am not sure but it might have to do with the way your flat forward
yield term structure and the yield-based bond functions measure the
time between the settlement date and the cashflow payment dates
effectively for the calculation of forward discount factors. The
former does

dayCounter(evalDate, cashflowDate) - dayCounter(evalDate, settlementDate)

whereas the latter

dayCounter(settlementDate, cashflowDate)

which might be different in your case for one or several cashflows. I
don't know the exact market convention for this but apparently the
latter is matching in your case and the former is slightly wrong.

You could check by replacing the day counter with another one for
which the two expressions above are identical for all cashflow dates
(of course the results get "wrong" then, but should be identical for
the different methods then).

Kind Regards
Peter


On 6 December 2016 at 19:39, Nick Pierce <[hidden email]> wrote:
Hi

I’m very new to Quantlib - about a week in (quite amazed so far!), but I
have managed to get some bonds working in Python 2.7, using SWIG. QL v 1.9.

I have set up a bond and the schedule is correct. When I calculate the clean
price by inputting a yield (using BondFunctions.cleanPrice(…pass in yield)),
this is correct (and yield from clean price), accrued is also correct and
duration is fine as yield is not far out.

However, when I attach a yield curve (flat forward) with the same yield, I
get a fractionally different / price yield calc. Dates in the schedule are
unadjusted, not modified following.

Results:

Clean Price Expected                     : 179.03
Clean Price BondFunctions.cleanPrice()   : 179.03
Clean Price bond.cleanPrice()            : 179.09
Difference                               : 0.0626431535518

Yield Expected                           : -0.0145461
Yield BondFunctions.BondYield()          : -0.0145461
Yield bond.bondYield()                   : -0.01455624
Difference                               : 1.01382016107e-05

Duration Expected                        : 34.52
Duration BondFunctions.duration()        : 34.52
Accrued Expected                         : 0.0483425414365
Accrued BondFunctions.accruedAmount()    : 0.0483425414365
Accrued bond.accruedAmount()             : 0.0483425414365
Program ended with exit code: 0

As you can see the calculated clean price from bond.cleanPrice() is 6 cents
different (incorrect)..and it’s been driving me mad!! The yield is different
from the 5th decimal place.

This is a real bond and the input numbers have been verified using a manual
calc in excel, they match the officially published numbers from the UK debt
management office (negative yield
is the real yield for an inflation linked gilt, i.e. the quote of 179.03 is
the market quote (clean) for the UKTI 52’s, before the index ratio is
applied).

In this example the price is the closing price for 30th Nov and settlement
date is 1st Dec, 2016.

If anyone can point out where I have gone wrong it would be much
appreciated.

Code is below.

Many thanks

Nick

import QuantLib as ql
import datetime as dt
calendar = ql.UnitedKingdom()
coupons=[0.0025]
exCouponPeriod=ql.Period(6,ql.Days)
calendarCoupon=ql.UnitedKingdom()
faceValue=100
businessConvention = ql.Unadjusted
businessConventionCoupon=ql.Unadjusted

today = ql.Date(30,11,2016)
evaluationDate = calendar.adjust(today)
issue_date = calendar.advance(evaluationDate,-1, ql.Years)
maturity_date = ql.Date(22,3,2052)

settlementDays=1
settlementDate=today+1
dayCount=ql.ActualActual(ql.ActualActual.ISMA)


ql.Settings.instance().setEvaluationDate(evaluationDate)

#input values to be checked
inputYield=-0.014546100
inputCleanPrice=179.03
inputDuration=34.52
inputAccrued=0.0483425414365


#create schedule
fixedSchedule =
ql.Schedule(issue_date,maturity_date,ql.Period(ql.Semiannual),calendar,ql.Unadjusted,ql.Unadjusted,ql.DateGeneration.Backward,False)

#create bond
fixed_rate_bond =
ql.FixedRateBond(settlementDays,faceValue,fixedSchedule,coupons,dayCount,businessConvention,100,issue_date,calendar,exCouponPeriod,calendarCoupon,businessConventionCoupon,False)

#curve
ts_curve=ql.FlatForward(evaluationDate, inputYield, dayCount, ql.Compounded,
ql.Semiannual)

#curve handle
ts_handle = ql.YieldTermStructureHandle(ts_curve)

#create bond engine
bond_engine = ql.DiscountingBondEngine(ts_handle)#set bond engine

#set pricing engine
fixed_rate_bond.setPricingEngine(bond_engine)

#calculate yield from clean price
#calculations using BondFunctions
bf_gry=ql.BondFunctions.bondYield(fixed_rate_bond,inputCleanPrice,dayCount,ql.Compounded,ql.Semiannual,settlementDate)
bf_cp=ql.BondFunctions.cleanPrice(fixed_rate_bond,bf_gry,dayCount,ql.Compounded,ql.Semiannual,settlementDate)
bf_ai=ql.BondFunctions.accruedAmount(fixed_rate_bond,settlementDate)
bf_md=ql.BondFunctions.duration(fixed_rate_bond,bf_gry,dayCount,ql.Compounded,ql.Semiannual,ql.Duration.Modified,settlementDate)

#Calculations using bond
b_gry=fixed_rate_bond.bondYield(dayCount,ql.Compounded,ql.Semiannual)
b_cp=fixed_rate_bond.cleanPrice()
b_ai=fixed_rate_bond.accruedAmount()

print "Clean Price Expected                     :",inputCleanPrice
print "Clean Price BondFunctions.cleanPrice()   :",round(bf_cp,2)
print "Clean Price bond.cleanPrice()            :",round(b_cp,2)
print "Difference                               :",b_cp-bf_cp
print ""
print "Yield Expected                           :",round(inputYield,8)
print "Yield BondFunctions.BondYield()          :",round(bf_gry,8)
print "Yield bond.bondYield()                   :",round(b_gry,8)
print "Difference                               :",bf_gry-b_gry
print ""

print "Duration Expected                        :",inputDuration
print "Duration BondFunctions.duration()        :",round(bf_md,2)

print "Accrued Expected                         :", inputAccrued
print "Accrued BondFunctions.accruedAmount()    :", bf_ai
print "Accrued bond.accruedAmount()             :", b_ai

------------------------------------------------------------------------------
Developer Access Program for Intel Xeon Phi Processors
Access to Intel Xeon Phi processor-based developer platforms.
With one year of Intel Parallel Studio XE.
Training and support from Colfax.
Order your platform today.http://sdm.link/xeonphi
_______________________________________________
QuantLib-users mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/quantlib-users



------------------------------------------------------------------------------
Developer Access Program for Intel Xeon Phi Processors
Access to Intel Xeon Phi processor-based developer platforms.
With one year of Intel Parallel Studio XE.
Training and support from Colfax.
Order your platform today.http://sdm.link/xeonphi
_______________________________________________
QuantLib-users mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/quantlib-users




------------------------------------------------------------------------------
Developer Access Program for Intel Xeon Phi Processors
Access to Intel Xeon Phi processor-based developer platforms.
With one year of Intel Parallel Studio XE.
Training and support from Colfax.
Order your platform today.http://sdm.link/xeonphi
_______________________________________________
QuantLib-users mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/quantlib-users
Reply | Threaded
Open this post in threaded view
|

Re: Question: small discrepancy in bond.cleanPrice() versus calling BondFunctions.cleanPrice() ?? Update - only ActualActual.ISMA is different, all other day counts OK.

Peter Caspers-4
I didn’t test anything either, but I guess the difference comes ultimately from the telescopic construction of discount factors in Cashflows::npv(…, yield, …) which is different from the reference date based discount factor calculation in YieldTermStructure for day counters with non-additive year fractions, look into cashflows.cpp L834:

    Real CashFlows::npv(const Leg& leg,
                        const InterestRate& y, ... ) {

...

/*  !!! telescopic construction of discount factors !!! */

            DiscountFactor b = y.discountFactor(lastDate, couponDate,
                                                refStartDate, refEndDate);
            discount *= b;
            lastDate = couponDate;

...




On 11 Dec 2016, at 21:34, Nick Pierce <[hidden email]> wrote:

Thanks Ferdinando

I made the change as you suggested, but alas it didn’t solve the problem, so I took a different approach and ran the same bond set up with a number of day counts in the curve and bond, as opposed to just the curve (using settlementDate).

The results are below (different bond, but generically the same problem):

The bond.cleanPrice() and BondFunctions.cleanPrice() return the same value in every case except the ActualAcual.ISMA.

I also changed the schedule generation and bond set up to be modified following instead of unadjusted, just in case it was a weekend problem, but it made no difference.

Does this suggest there is a bug in the ISMA generation of a curve??

Any further help much appreciated.

Thanks

Nick

The result we are expecting is 179.03 (round to 2 d.p), using ISMA.

Actual/Actual (ISMA) day counter
bond.cleanPrice()=           179.099809062 <<<<this is the wrong one
BondFunctions.cleanPrice()=  179.029981553 <<<<correct when rounded to 2 decimal places
Actual/Actual (ISDA) day counter
bond.cleanPrice()=           179.028406531
BondFunctions.cleanPrice()=  179.028406531
Actual/365 (Fixed) day counter
bond.cleanPrice()=           179.086145534
BondFunctions.cleanPrice()=  179.086145534
Actual/365 (NL) day counter
bond.cleanPrice()=           179.023736844
BondFunctions.cleanPrice()=  179.023736844
30/360 (Bond Basis) day counter
bond.cleanPrice()=           179.034435254
BondFunctions.cleanPrice()=  179.034435254

On 9 Dec 2016, at 07:20, Ferdinando M. Ametrano <[hidden email]> wrote:

I cannot test it right now, but the issue might be solved if instead of
ts_curve=ql.FlatForward(evaluationDate, inputYield, dayCount, ql.Compounded, ql.Semiannual)

you would use
ts_curve=ql.FlatForward(settlementDate, inputYield, dayCount, ql.Compounded, ql.Semiannual)

as the latter is the proper way to set the time origin in your case


On Tue, Dec 6, 2016 at 9:17 PM, Nick Pierce <[hidden email]> wrote:
Hi Peter

Thanks for the quick response, much appreciated.

Yes I think you are correct…

using

dayCount=ql.ActualActual(ql.ActualActual.ISDA)
Clean Price Expected                     : 167.05
Clean Price BondFunctions.cleanPrice()   : 167.05
Clean Price bond.cleanPrice()            : 167.06
Difference                               : 0.00866617031875

Yield Expected                           : 0.01835696
Yield BondFunctions.BondYield()          : 0.01836077
Yield bond.bondYield()                   : 0.01835696
Difference                               : 3.81175596394e-06

using

dayCount=ql.Thirty360()
Clean Price Expected                     : 167.05
Clean Price BondFunctions.cleanPrice()   : 167.05
Clean Price bond.cleanPrice()            : 167.06
Difference                               : 0.00935696611168

Yield Expected                           : 0.01835696
Yield BondFunctions.BondYield()          : 0.01836097
Yield bond.bondYield()                   : 0.01835696
Difference                               : 4.01397533715e-06

So in short the BondFunctions call is unaffected by the change in dayCounter and correct in all cases.

However, the bond.cleanPrice() changes with the change in dayCounter and is ironically closer (less than 1 cent out) versus the original 6 cents.

..so my next daft question is…

Is there anyway to fix this and should they be consistent??? 

Thanks again.

Regards

Nick



On 6 Dec 2016, at 19:55, Peter Caspers <[hidden email]> wrote:

Hi Nick,

I am not sure but it might have to do with the way your flat forward
yield term structure and the yield-based bond functions measure the
time between the settlement date and the cashflow payment dates
effectively for the calculation of forward discount factors. The
former does

dayCounter(evalDate, cashflowDate) - dayCounter(evalDate, settlementDate)

whereas the latter

dayCounter(settlementDate, cashflowDate)

which might be different in your case for one or several cashflows. I
don't know the exact market convention for this but apparently the
latter is matching in your case and the former is slightly wrong.

You could check by replacing the day counter with another one for
which the two expressions above are identical for all cashflow dates
(of course the results get "wrong" then, but should be identical for
the different methods then).

Kind Regards
Peter


On 6 December 2016 at 19:39, Nick Pierce <[hidden email]> wrote:
Hi

I’m very new to Quantlib - about a week in (quite amazed so far!), but I
have managed to get some bonds working in Python 2.7, using SWIG. QL v 1.9.

I have set up a bond and the schedule is correct. When I calculate the clean
price by inputting a yield (using BondFunctions.cleanPrice(…pass in yield)),
this is correct (and yield from clean price), accrued is also correct and
duration is fine as yield is not far out.

However, when I attach a yield curve (flat forward) with the same yield, I
get a fractionally different / price yield calc. Dates in the schedule are
unadjusted, not modified following.

Results:

Clean Price Expected                     : 179.03
Clean Price BondFunctions.cleanPrice()   : 179.03
Clean Price bond.cleanPrice()            : 179.09
Difference                               : 0.0626431535518

Yield Expected                           : -0.0145461
Yield BondFunctions.BondYield()          : -0.0145461
Yield bond.bondYield()                   : -0.01455624
Difference                               : 1.01382016107e-05

Duration Expected                        : 34.52
Duration BondFunctions.duration()        : 34.52
Accrued Expected                         : 0.0483425414365
Accrued BondFunctions.accruedAmount()    : 0.0483425414365
Accrued bond.accruedAmount()             : 0.0483425414365
Program ended with exit code: 0

As you can see the calculated clean price from bond.cleanPrice() is 6 cents
different (incorrect)..and it’s been driving me mad!! The yield is different
from the 5th decimal place.

This is a real bond and the input numbers have been verified using a manual
calc in excel, they match the officially published numbers from the UK debt
management office (negative yield
is the real yield for an inflation linked gilt, i.e. the quote of 179.03 is
the market quote (clean) for the UKTI 52’s, before the index ratio is
applied).

In this example the price is the closing price for 30th Nov and settlement
date is 1st Dec, 2016.

If anyone can point out where I have gone wrong it would be much
appreciated.

Code is below.

Many thanks

Nick

import QuantLib as ql
import datetime as dt
calendar = ql.UnitedKingdom()
coupons=[0.0025]
exCouponPeriod=ql.Period(6,ql.Days)
calendarCoupon=ql.UnitedKingdom()
faceValue=100
businessConvention = ql.Unadjusted
businessConventionCoupon=ql.Unadjusted

today = ql.Date(30,11,2016)
evaluationDate = calendar.adjust(today)
issue_date = calendar.advance(evaluationDate,-1, ql.Years)
maturity_date = ql.Date(22,3,2052)

settlementDays=1
settlementDate=today+1
dayCount=ql.ActualActual(ql.ActualActual.ISMA)


ql.Settings.instance().setEvaluationDate(evaluationDate)

#input values to be checked
inputYield=-0.014546100
inputCleanPrice=179.03
inputDuration=34.52
inputAccrued=0.0483425414365


#create schedule
fixedSchedule =
ql.Schedule(issue_date,maturity_date,ql.Period(ql.Semiannual),calendar,ql.Unadjusted,ql.Unadjusted,ql.DateGeneration.Backward,False)

#create bond
fixed_rate_bond =
ql.FixedRateBond(settlementDays,faceValue,fixedSchedule,coupons,dayCount,businessConvention,100,issue_date,calendar,exCouponPeriod,calendarCoupon,businessConventionCoupon,False)

#curve
ts_curve=ql.FlatForward(evaluationDate, inputYield, dayCount, ql.Compounded,
ql.Semiannual)

#curve handle
ts_handle = ql.YieldTermStructureHandle(ts_curve)

#create bond engine
bond_engine = ql.DiscountingBondEngine(ts_handle)#set bond engine

#set pricing engine
fixed_rate_bond.setPricingEngine(bond_engine)

#calculate yield from clean price
#calculations using BondFunctions
bf_gry=ql.BondFunctions.bondYield(fixed_rate_bond,inputCleanPrice,dayCount,ql.Compounded,ql.Semiannual,settlementDate)
bf_cp=ql.BondFunctions.cleanPrice(fixed_rate_bond,bf_gry,dayCount,ql.Compounded,ql.Semiannual,settlementDate)
bf_ai=ql.BondFunctions.accruedAmount(fixed_rate_bond,settlementDate)
bf_md=ql.BondFunctions.duration(fixed_rate_bond,bf_gry,dayCount,ql.Compounded,ql.Semiannual,ql.Duration.Modified,settlementDate)

#Calculations using bond
b_gry=fixed_rate_bond.bondYield(dayCount,ql.Compounded,ql.Semiannual)
b_cp=fixed_rate_bond.cleanPrice()
b_ai=fixed_rate_bond.accruedAmount()

print "Clean Price Expected                     :",inputCleanPrice
print "Clean Price BondFunctions.cleanPrice()   :",round(bf_cp,2)
print "Clean Price bond.cleanPrice()            :",round(b_cp,2)
print "Difference                               :",b_cp-bf_cp
print ""
print "Yield Expected                           :",round(inputYield,8)
print "Yield BondFunctions.BondYield()          :",round(bf_gry,8)
print "Yield bond.bondYield()                   :",round(b_gry,8)
print "Difference                               :",bf_gry-b_gry
print ""

print "Duration Expected                        :",inputDuration
print "Duration BondFunctions.duration()        :",round(bf_md,2)

print "Accrued Expected                         :", inputAccrued
print "Accrued BondFunctions.accruedAmount()    :", bf_ai
print "Accrued bond.accruedAmount()             :", b_ai

------------------------------------------------------------------------------
Developer Access Program for Intel Xeon Phi Processors
Access to Intel Xeon Phi processor-based developer platforms.
With one year of Intel Parallel Studio XE.
Training and support from Colfax.
Order your platform today.http://sdm.link/xeonphi
_______________________________________________
QuantLib-users mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/quantlib-users



------------------------------------------------------------------------------
Developer Access Program for Intel Xeon Phi Processors
Access to Intel Xeon Phi processor-based developer platforms.
With one year of Intel Parallel Studio XE.
Training and support from Colfax.
Order your platform today.http://sdm.link/xeonphi
_______________________________________________
QuantLib-users mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/quantlib-users





------------------------------------------------------------------------------
Developer Access Program for Intel Xeon Phi Processors
Access to Intel Xeon Phi processor-based developer platforms.
With one year of Intel Parallel Studio XE.
Training and support from Colfax.
Order your platform today.http://sdm.link/xeonphi
_______________________________________________
QuantLib-users mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/quantlib-users
Reply | Threaded
Open this post in threaded view
|

Re: Question: small discrepancy in bond.cleanPrice() versus calling BondFunctions.cleanPrice() ?? Update - only ActualActual.ISMA is different, all other day counts OK.

Luigi Ballabio
I'm in the didn't-test crowd too, but I think it might be the same issue as <http://quant.stackexchange.com/questions/12707/pricing-a-fixedratebond-in-quantlib-yield-vs-termstructure>. There's a longer explanation there.

Luigi


On Mon, Dec 12, 2016 at 8:57 AM Peter Caspers <[hidden email]> wrote:
I didn’t test anything either, but I guess the difference comes ultimately from the telescopic construction of discount factors in Cashflows::npv(…, yield, …) which is different from the reference date based discount factor calculation in YieldTermStructure for day counters with non-additive year fractions, look into cashflows.cpp L834:

    Real CashFlows::npv(const Leg& leg,
                        const InterestRate& y, ... ) {

...

/*  !!! telescopic construction of discount factors !!! */

            DiscountFactor b = y.discountFactor(lastDate, couponDate,
                                                refStartDate, refEndDate);
            discount *= b;
            lastDate = couponDate;

...




On 11 Dec 2016, at 21:34, Nick Pierce <[hidden email]> wrote:

Thanks Ferdinando

I made the change as you suggested, but alas it didn’t solve the problem, so I took a different approach and ran the same bond set up with a number of day counts in the curve and bond, as opposed to just the curve (using settlementDate).

The results are below (different bond, but generically the same problem):

The bond.cleanPrice() and BondFunctions.cleanPrice() return the same value in every case except the ActualAcual.ISMA.

I also changed the schedule generation and bond set up to be modified following instead of unadjusted, just in case it was a weekend problem, but it made no difference.

Does this suggest there is a bug in the ISMA generation of a curve??

Any further help much appreciated.

Thanks

Nick

The result we are expecting is 179.03 (round to 2 d.p), using ISMA.

Actual/Actual (ISMA) day counter
bond.cleanPrice()=           179.099809062 <<<<this is the wrong one
BondFunctions.cleanPrice()=  179.029981553 <<<<correct when rounded to 2 decimal places
Actual/Actual (ISDA) day counter
bond.cleanPrice()=           179.028406531
BondFunctions.cleanPrice()=  179.028406531
Actual/365 (Fixed) day counter
bond.cleanPrice()=           179.086145534
BondFunctions.cleanPrice()=  179.086145534
Actual/365 (NL) day counter
bond.cleanPrice()=           179.023736844
BondFunctions.cleanPrice()=  179.023736844
30/360 (Bond Basis) day counter
bond.cleanPrice()=           179.034435254
BondFunctions.cleanPrice()=  179.034435254

On 9 Dec 2016, at 07:20, Ferdinando M. Ametrano <[hidden email]> wrote:

I cannot test it right now, but the issue might be solved if instead of
ts_curve=ql.FlatForward(evaluationDate, inputYield, dayCount, ql.Compounded, ql.Semiannual)

you would use
ts_curve=ql.FlatForward(settlementDate, inputYield, dayCount, ql.Compounded, ql.Semiannual)

as the latter is the proper way to set the time origin in your case


On Tue, Dec 6, 2016 at 9:17 PM, Nick Pierce <[hidden email]> wrote:
Hi Peter

Thanks for the quick response, much appreciated.

Yes I think you are correct…

using

dayCount=ql.ActualActual(ql.ActualActual.ISDA)
Clean Price Expected                     : 167.05
Clean Price BondFunctions.cleanPrice()   : 167.05
Clean Price bond.cleanPrice()            : 167.06
Difference                               : 0.00866617031875

Yield Expected                           : 0.01835696
Yield BondFunctions.BondYield()          : 0.01836077
Yield bond.bondYield()                   : 0.01835696
Difference                               : 3.81175596394e-06

using

dayCount=ql.Thirty360()
Clean Price Expected                     : 167.05
Clean Price BondFunctions.cleanPrice()   : 167.05
Clean Price bond.cleanPrice()            : 167.06
Difference                               : 0.00935696611168

Yield Expected                           : 0.01835696
Yield BondFunctions.BondYield()          : 0.01836097
Yield bond.bondYield()                   : 0.01835696
Difference                               : 4.01397533715e-06

So in short the BondFunctions call is unaffected by the change in dayCounter and correct in all cases.

However, the bond.cleanPrice() changes with the change in dayCounter and is ironically closer (less than 1 cent out) versus the original 6 cents.

..so my next daft question is…

Is there anyway to fix this and should they be consistent??? 

Thanks again.

Regards

Nick



On 6 Dec 2016, at 19:55, Peter Caspers <[hidden email]> wrote:

Hi Nick,

I am not sure but it might have to do with the way your flat forward
yield term structure and the yield-based bond functions measure the
time between the settlement date and the cashflow payment dates
effectively for the calculation of forward discount factors. The
former does

dayCounter(evalDate, cashflowDate) - dayCounter(evalDate, settlementDate)

whereas the latter

dayCounter(settlementDate, cashflowDate)

which might be different in your case for one or several cashflows. I
don't know the exact market convention for this but apparently the
latter is matching in your case and the former is slightly wrong.

You could check by replacing the day counter with another one for
which the two expressions above are identical for all cashflow dates
(of course the results get "wrong" then, but should be identical for
the different methods then).

Kind Regards
Peter


On 6 December 2016 at 19:39, Nick Pierce <[hidden email]> wrote:
Hi

I’m very new to Quantlib - about a week in (quite amazed so far!), but I
have managed to get some bonds working in Python 2.7, using SWIG. QL v 1.9.

I have set up a bond and the schedule is correct. When I calculate the clean
price by inputting a yield (using BondFunctions.cleanPrice(…pass in yield)),
this is correct (and yield from clean price), accrued is also correct and
duration is fine as yield is not far out.

However, when I attach a yield curve (flat forward) with the same yield, I
get a fractionally different / price yield calc. Dates in the schedule are
unadjusted, not modified following.

Results:

Clean Price Expected                     : 179.03
Clean Price BondFunctions.cleanPrice()   : 179.03
Clean Price bond.cleanPrice()            : 179.09
Difference                               : 0.0626431535518

Yield Expected                           : -0.0145461
Yield BondFunctions.BondYield()          : -0.0145461
Yield bond.bondYield()                   : -0.01455624
Difference                               : 1.01382016107e-05

Duration Expected                        : 34.52
Duration BondFunctions.duration()        : 34.52
Accrued Expected                         : 0.0483425414365
Accrued BondFunctions.accruedAmount()    : 0.0483425414365
Accrued bond.accruedAmount()             : 0.0483425414365
Program ended with exit code: 0

As you can see the calculated clean price from bond.cleanPrice() is 6 cents
different (incorrect)..and it’s been driving me mad!! The yield is different
from the 5th decimal place.

This is a real bond and the input numbers have been verified using a manual
calc in excel, they match the officially published numbers from the UK debt
management office (negative yield
is the real yield for an inflation linked gilt, i.e. the quote of 179.03 is
the market quote (clean) for the UKTI 52’s, before the index ratio is
applied).

In this example the price is the closing price for 30th Nov and settlement
date is 1st Dec, 2016.

If anyone can point out where I have gone wrong it would be much
appreciated.

Code is below.

Many thanks

Nick

import QuantLib as ql
import datetime as dt
calendar = ql.UnitedKingdom()
coupons=[0.0025]
exCouponPeriod=ql.Period(6,ql.Days)
calendarCoupon=ql.UnitedKingdom()
faceValue=100
businessConvention = ql.Unadjusted
businessConventionCoupon=ql.Unadjusted

today = ql.Date(30,11,2016)
evaluationDate = calendar.adjust(today)
issue_date = calendar.advance(evaluationDate,-1, ql.Years)
maturity_date = ql.Date(22,3,2052)

settlementDays=1
settlementDate=today+1
dayCount=ql.ActualActual(ql.ActualActual.ISMA)


ql.Settings.instance().setEvaluationDate(evaluationDate)

#input values to be checked
inputYield=-0.014546100
inputCleanPrice=179.03
inputDuration=34.52
inputAccrued=0.0483425414365


#create schedule
fixedSchedule =
ql.Schedule(issue_date,maturity_date,ql.Period(ql.Semiannual),calendar,ql.Unadjusted,ql.Unadjusted,ql.DateGeneration.Backward,False)

#create bond
fixed_rate_bond =
ql.FixedRateBond(settlementDays,faceValue,fixedSchedule,coupons,dayCount,businessConvention,100,issue_date,calendar,exCouponPeriod,calendarCoupon,businessConventionCoupon,False)

#curve
ts_curve=ql.FlatForward(evaluationDate, inputYield, dayCount, ql.Compounded,
ql.Semiannual)

#curve handle
ts_handle = ql.YieldTermStructureHandle(ts_curve)

#create bond engine
bond_engine = ql.DiscountingBondEngine(ts_handle)#set bond engine

#set pricing engine
fixed_rate_bond.setPricingEngine(bond_engine)

#calculate yield from clean price
#calculations using BondFunctions
bf_gry=ql.BondFunctions.bondYield(fixed_rate_bond,inputCleanPrice,dayCount,ql.Compounded,ql.Semiannual,settlementDate)
bf_cp=ql.BondFunctions.cleanPrice(fixed_rate_bond,bf_gry,dayCount,ql.Compounded,ql.Semiannual,settlementDate)
bf_ai=ql.BondFunctions.accruedAmount(fixed_rate_bond,settlementDate)
bf_md=ql.BondFunctions.duration(fixed_rate_bond,bf_gry,dayCount,ql.Compounded,ql.Semiannual,ql.Duration.Modified,settlementDate)

#Calculations using bond
b_gry=fixed_rate_bond.bondYield(dayCount,ql.Compounded,ql.Semiannual)
b_cp=fixed_rate_bond.cleanPrice()
b_ai=fixed_rate_bond.accruedAmount()

print "Clean Price Expected                     :",inputCleanPrice
print "Clean Price BondFunctions.cleanPrice()   :",round(bf_cp,2)
print "Clean Price bond.cleanPrice()            :",round(b_cp,2)
print "Difference                               :",b_cp-bf_cp
print ""
print "Yield Expected                           :",round(inputYield,8)
print "Yield BondFunctions.BondYield()          :",round(bf_gry,8)
print "Yield bond.bondYield()                   :",round(b_gry,8)
print "Difference                               :",bf_gry-b_gry
print ""

print "Duration Expected                        :",inputDuration
print "Duration BondFunctions.duration()        :",round(bf_md,2)

print "Accrued Expected                         :", inputAccrued
print "Accrued BondFunctions.accruedAmount()    :", bf_ai
print "Accrued bond.accruedAmount()             :", b_ai

------------------------------------------------------------------------------
Developer Access Program for Intel Xeon Phi Processors
Access to Intel Xeon Phi processor-based developer platforms.
With one year of Intel Parallel Studio XE.
Training and support from Colfax.
Order your platform today.http://sdm.link/xeonphi
_______________________________________________
QuantLib-users mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/quantlib-users



------------------------------------------------------------------------------
Developer Access Program for Intel Xeon Phi Processors
Access to Intel Xeon Phi processor-based developer platforms.
With one year of Intel Parallel Studio XE.
Training and support from Colfax.
Order your platform today.http://sdm.link/xeonphi
_______________________________________________
QuantLib-users mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/quantlib-users




------------------------------------------------------------------------------
Developer Access Program for Intel Xeon Phi Processors
Access to Intel Xeon Phi processor-based developer platforms.
With one year of Intel Parallel Studio XE.
Training and support from Colfax.
Order your platform today.http://sdm.link/xeonphi_______________________________________________
QuantLib-users mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/quantlib-users

------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, SlashDot.org! http://sdm.link/slashdot
_______________________________________________
QuantLib-users mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/quantlib-users