简体   繁体   中英

Bond cashflows only from evaluation date onwards

Suppose the evaluation date is today, and there is a floating rate bond with any issue date in the past. I would like to use the cashflows() method of the Bond class (or FloatingRateBond ) to compute cashflows only from the evaluation date onwards. The problem is, neither do I have market data of the past (for interest rate fixing), nor am I interested in interest rate cashflows that occurred in the past.
I know about the hasOccurred method of CashFlow but that doesn't help me, since QuantLib throws an error that it misses market data for the past when I evoke cashflows() .
Can I tell cashflows() to only calculate the cashflows occuring after a certain date?

The following code reproduces the error
RuntimeError: Missing EoniaON Actual/360 fixing for December 29th, 2017

import QuantLib as ql

referenceDate = ql.Date(1,1,2019)
ql.Settings.instance().evaluationDate = referenceDate

##################
# curve definition
##################
# curve data
periods = [ql.Period("1D"), ql.Period("2D"), ql.Period("3D"), ql.Period("1W"), ql.Period("2W"), ql.Period("4W3D"), ql.Period("8W5D"), ql.Period("12W6D"), ql.Period("17W2D"), ql.Period("21W4D"), ql.Period("26W"), ql.Period("30W2D"), ql.Period("34W5D"), ql.Period("39W1D"), ql.Period("43W3D"), ql.Period("47W6D"), ql.Period("52W1D"), ql.Period("65W"), ql.Period("78W1D"), ql.Period("91W2D"), ql.Period("104W2D"), ql.Period("117W2D"), ql.Period("130W3D"), ql.Period("143W4D"), ql.Period("156W4D"), ql.Period("169W3D"), ql.Period("182W4D"), ql.Period("195W5D"), ql.Period("208W5D"), ql.Period("221W4D"), ql.Period("234W5D"), ql.Period("247W6D"), ql.Period("260W6D"), ql.Period("273W5D"), ql.Period("286W6D"), ql.Period("300W"), ql.Period("313W"), ql.Period("326W"), ql.Period("339W1D"), ql.Period("352W2D"), ql.Period("365W2D"), ql.Period("378W1D"), ql.Period("391W2D"), ql.Period("404W3D"), ql.Period("417W3D"), ql.Period("430W2D"), ql.Period("443W3D"), ql.Period("456W4D"), ql.Period("469W4D"), ql.Period("482W3D"), ql.Period("495W4D"), ql.Period("508W5D"), ql.Period("521W5D"), ql.Period("534W5D"), ql.Period("547W6D"), ql.Period("561W"), ql.Period("574W"), ql.Period("586W6D"), ql.Period("600W"), ql.Period("613W1D"), ql.Period("626W1D"), ql.Period("639W"), ql.Period("652W1D"), ql.Period("665W2D"), ql.Period("678W2D"), ql.Period("691W1D"), ql.Period("704W2D"), ql.Period("717W3D"), ql.Period("730W3D"), ql.Period("743W3D"), ql.Period("756W4D"), ql.Period("769W5D"), ql.Period("782W5D"), ql.Period("834W6D"), ql.Period("887W"), ql.Period("939W1D"), ql.Period("991W3D"), ql.Period("1043W4D"), ql.Period("1095W5D"), ql.Period("1147W6D"), ql.Period("1200W1D"), ql.Period("1252W2D"), ql.Period("1304W3D"), ql.Period("1356W4D"), ql.Period("1408W6D"), ql.Period("1461W"), ql.Period("1513W1D"), ql.Period("1565W2D"), ql.Period("1617W4D"), ql.Period("1669W5D"), ql.Period("1721W6D"), ql.Period("1774W"), ql.Period("1826W2D"), ql.Period("1878W3D"), ql.Period("1930W4D"), ql.Period("1982W5D"), ql.Period("2035W"), ql.Period("2087W1D"), ql.Period("2139W2D"), ql.Period("2191W3D"), ql.Period("2243W5D"), ql.Period("2295W6D"), ql.Period("2348W"), ql.Period("2400W1D"), ql.Period("2452W3D"), ql.Period("2504W4D"), ql.Period("2556W5D"), ql.Period("2608W6D")]
zeroRates = [-0.003509, -0.0035994, -0.0035994, -0.003583013, -0.003552871, -0.0035322829999999998, -0.003516268, -0.0034983690000000003, -0.0035032929999999998, -0.003504438, -0.003495866, -0.0034945840000000002, -0.003478077, -0.00345774, -0.0034395790000000003, -0.003427, -0.0034006059999999996, -0.0033090059999999998, -0.003129996, -0.002900087, -0.002612546, -0.0022843539999999997, -0.0019280470000000002, -0.001559065, -0.001193597, -0.000840967, -0.000490511, -0.00014884299999999998, 0.00018241900000000002, 0.0005049, 0.000830168, 0.001151439, 0.0014659279999999998, 0.0017742779999999998, 0.002086053, 0.002393539, 0.0026927709999999996, 0.002986763, 0.003279388, 0.003568822, 0.003853275, 0.004133911, 0.0044197740000000004, 0.004703852, 0.004982476, 0.005255206, 0.005530559, 0.005802013000000001, 0.0060663869999999995, 0.006323573000000001, 0.006582067, 0.006836485, 0.0070842959999999995, 0.0073285880000000005, 0.007571736999999999, 0.007810716999999999, 0.008042855, 0.008267911, 0.00849312, 0.008713048000000001, 0.008925159, 0.009129278000000001, 0.009332287, 0.009529535, 0.009719003, 0.009901006, 0.010081563, 0.010256504, 0.010424104, 0.010586399, 0.010745208000000001, 0.010898543, 0.011044967, 0.011580671, 0.012041008, 0.012432549, 0.012763392, 0.013038589, 0.013267155, 0.013454629, 0.0136077, 0.013731867, 0.013833949, 0.013920055, 0.013993108, 0.014054918999999999, 0.014108182, 0.014155454, 0.014198216999999999, 0.014236871000000002, 0.01427123, 0.014301092, 0.014326257, 0.014346227, 0.014360703, 0.014369678, 0.014372562, 0.014369345, 0.01435993, 0.014344611, 0.014324081, 0.014299043999999999, 0.014270096999999999, 0.014237841999999999, 0.014202977, 0.014166105, 0.014128021999999999, 0.01408933]

# create quotes from rates so that they are observable (well, but the quotes aren't modified in this example code)
quotes = []
for rate in zeroRates:
    q = ql.SimpleQuote(rate)
    quotes.append(q)

nodes = zip(periods, quotes)

zcHelpers = [ql.DepositRateHelper(ql.QuoteHandle(quote), tenor,
                                    0, ql.UnitedStates(), ql.Unadjusted,
                                    True, ql.Actual365Fixed())
            for (tenor, quote) in nodes ]

curve = ql.PiecewiseLinearZero(referenceDate, zcHelpers, ql.Actual365Fixed())

curveHandle = ql.RelinkableYieldTermStructureHandle()
curveHandle.linkTo(curve)

index = ql.Eonia(curveHandle)
##################
######## end curve
##################

#################
# bond definition
#################

schedule =  ql.Schedule(referenceDate - ql.Period("1Y"), # effective date
                        referenceDate + ql.Period("5Y"), # termination date
                        ql.Period("3M"), # tenor
                        ql.UnitedStates(), # calendar 
                        ql.Unadjusted, # convention
                        ql.Unadjusted, # terminationDateConvention
                        ql.DateGeneration.Backward, # date generation rule 
                        True # end of month
                        )

bond_leg = ql.FloatingRateBond(0, # settlementDays,,
                                500000, # current notional
                                schedule, # schedule
                                index, 
                                ql.Actual365Fixed(), # accrual day count convention
                                ql.Unadjusted, # payment convention
                                spreads=[0.0], # spreads                  
                                inArrears=False, # in arrears
                                redemption=100, # redemption
                                issueDate=referenceDate - ql.Period("1Y") # issuance date
                                )

# set pricing engine
bondEngine = ql.DiscountingBondEngine(curveHandle)
bond_leg.setPricingEngine(bondEngine)

# coupon pricers
pricer = ql.BlackIborCouponPricer()

# optionlet volatilities
vol = ql.ConstantOptionletVolatility(0, # settlementDays
                                    ql.UnitedStates(), # calendar
                                    ql.Unadjusted, # payment convention
                                    0.0, # volatility
                                    ql.Actual365Fixed()) # accrual day count

pricer.setCapletVolatility(ql.OptionletVolatilityStructureHandle(vol))
ql.setCouponPricer(bond_leg.cashflows(), pricer)

print([(c.date(), c.amount()) for c in bond_leg.cashflows()])

It's not cashflows() that raises the error; you can see it by rewriting your last line as

cfs = bond_leg.cashflows()
print([(c.date(), c.amount()) for c in cfs])

the error occurs on the second line, not the first.

The cashflows() method doesn't filter its results by date, but you can do it before calling amount() . Something like

cfs = bond_leg.cashflows()
min_date = referenceDate + ql.Period("6M")
print([(c.date(), c.amount()) for c in cfs if c.date() >= min_date])

will work based on the CashFlow interface. If you want more information, you can use one of the functions available for downcasting cash flows; but in that case, you'll also have to check for the possibility that the cast fails if the cash flow is not a floating-rate one (this happens, eg, for the redemption):

for cf in bond_leg.cashflows():
    c = ql.as_floating_rate_coupon(cf)
    if c and c.fixingDate() >= referenceDate:
        print(c.date(), c.amount())

(the if c checks for a valid cast).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM