eddelbuettel / rquantlib

R interface to the QuantLib library
119 stars 50 forks source link

Differences in Schedule and FixedRateBond #189

Open tomasnobrega opened 3 months ago

tomasnobrega commented 3 months ago

Hello, I'm trying to price a sample bond, but for some reason, the schedule object differs when I run it within Schedule() compared to when I use a list inside FixedRateBond(). Specifically, the bond function is omitting the first cashflow. Am I missing something? Is this the expected behaviour?

Schedule Function

> schedule <- Schedule(
 list(
     effectiveDate=as.Date("2023-06-01"),
     maturityDate=as.Date("2024-12-01"),
     period='Semiannual',
     calendar='UnitedStates/Settlement',
     businessDayConvention='Unadjusted',
     terminationDateConvention='Unadjusted',
     dateGeneration='Forward',
     endOfMonth=0
 ))
> schedule
[1] "2023-06-01" "2023-12-01" "2024-06-01" "2024-12-01"

FixedRateBond

schedule <- 
  list(
    effectiveDate=as.Date("2023-06-01"),
    maturityDate=as.Date("2024-12-01"),
    period='Semiannual',
    calendar='UnitedStates/Settlement',
    businessDayConvention='Unadjusted',
    terminationDateConvention='Unadjusted',
    dateGeneration='Forward',
    endOfMonth=1
)
#Create the discount rate curve to compute present values
date <- as.Date("2023-05-04")
discountCurve <- DiscountCurve(
  list(
    tradeDate = date,
    settleDate = date + 2,
    dt = .25,
    interpWhat="zero",
    interpHow="linear"
  ),
  tsQuotes = list(flat = 0.042675)
)
#Create the bond
bondinfo <- list(
  settlementDays = 2
)
setEvaluationDate(as.Date("2023-05-04"))
FixedRateBond(
  bondinfo,
  rates = c(0.085),
  schedule,
  list(
    dayCounter="Thirty360",
    compounding='Compounded',
    freq='Semiannual',
    durationType='Modified'
  ),
  discountCurve = discountCurve
)

Which gives output:

Concise summary of valuation for FixedRateBond 
 Net present value :  105.67 
       clean price :  105.69 
       dirty price :  105.69 
    accrued coupon :  0 
             yield :  0.043301 
          duration :  1.4762 
   settlement date :  2023-05-08 
        cash flows : 
       Date Amount
 2023-12-01   4.25
 2024-06-03   4.25
 2024-12-02   4.25
 2024-12-02 100.00
eddelbuettel commented 3 months ago

I don't understand your question.

tomasnobrega commented 3 months ago

In the schedule part, there is a schedule for "2023-06-01" however the bond function gives a cash-flow starting at "2023-12-01". Why is the bond function skipping the first schedule if the arguments inside schedule are the same?

eddelbuettel commented 3 months ago

I see. Good question. You would have to dig into the code to see if we drop something somewhere.

I usually start from the self-contained C++ examples.

eddelbuettel commented 3 months ago

It's been too long since I worked on a bond desk but I would tend to agree with you that there should be an initial June cash flow. The cash flow vector 'generator' (in utils.cpp) look fine (no off by one one error) so it may be the schedule -- but if so then all the pricers would be off. Some I am puzzled.

I don't have access to benchmarking tools (ie Bloomberg or alike) so I can't help on that side.

tomasnobrega commented 3 months ago

Thank you. I've modified the code to initiate the schedule a semester earlier, and now the price closely aligns with the benchmark. I suspect the schedule accounts for the first period as an issuance or a type of "non-payment" start date, although I'm not sure.

eddelbuettel commented 3 months ago

I also noticed that this uses the 'discount curve given' pricer when the alternates (for price or yield) may be more common. And I am with you that it is probably something intrinsic to the Schedule class.

eddelbuettel commented 1 month ago

Hey @lballabio I was wondering if I could ever so gingerly lean on you here. As @tomasnobrega documents above, the schedule 'looks wrong'. This is pretty old code but I could walk you through the C++ (standard, if possibly dated, QL use). The call is an optional one -- we otherwise get into the worker function with a different setup. But if you could glance at the question / issue for brief moment maybe you can come up with something I could purse?

lballabio commented 1 month ago

Hi Dirk, Tomas,

the initial June date is interpreted by the code as the start of the bond, not the date of the first cashflow. That is, in the generated schedule the first date is meant to be the start of the first coupon, the second date is the end of the first coupon and the start of the second, and so on; otherwise we wouldn't know when the first coupon starts accruing.

The summary from the bond, instead, is showing the cash flow dates, as in the payment dates (the first business day after the end of each coupon); this corresponds to the schedule dates from the second one onwards.

As Tomas already noticed, adding the start date—one semester before the first coupon date—gives the bond you wanted.

eddelbuettel commented 1 month ago

Thanks for reporting back so promptly. I have to find a moment to 'think and/or play' as it seems like my print method is wrong here?

lballabio commented 1 month ago

No, if the intent is to print out dates and amounts of the cash flows, your print method is correct. What is probably confusing is that unlike the others, the first date in the schedule is not a cash flow date but the start of the first coupon.

If you wanted to make it more clear (but less concise), you might output something like

       cash flows : 
 Start date    End date    Payment date   Amount
 2023-06-01  2023-12-01      2023-12-01     4.25
 2023-12-01  2024-06-01      2024-06-03     4.25
 2024-06-01  2024-12-01      2024-12-02     4.25
                             2024-12-02   100.00

instead of just the last two columns as you're doing now. But that might be confusing in other ways :)