eddelbuettel / rquantlib

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

DividendVanillaOption #72

Closed hfty closed 8 years ago

hfty commented 8 years ago

I would like to get prices and Greeks for American options with discrete dividends, which seems to be implemented in QuantLib in DividendVanillaOption.

It is my understanding that this is not currently part of the code covered by RQuantLib. Is there any way I could easily interface that code from R? My C++ is fairly limited, and the QuantLib codebase looks like a beast.

eddelbuettel commented 8 years ago

The QuantLib codebase is a beast! But there is some hope -- by studying the examples and particularly the (very extensive) unit tests you can often come up with something simple. Get that working standalone, and from there interfacing from R is pretty easy thanks to Rcpp.

Sounds like a plan? I can help here and there...

hfty commented 8 years ago

Thanks, I can try and follow that route. On the RQuantLib side, what is the type of interface that would be most appropriate? Should I go with a DividendAmericanOption function alongside AmericanOption, or modify AmericanOption to include optional parameters?

eddelbuettel commented 8 years ago

If DividendAmericanOption is different (enough) from AmericanOption then it may make the most sense to make it a different function.

tleitch commented 8 years ago

If it were me, I'd do an AmericanOption and have an optional dividend argument with a default of 0 and have a single interface.

scchess commented 8 years ago

@eddelbuettel Thus sounds like an easy problem for you. Why wouldn't you just do it?

tleitch commented 8 years ago

I'm working on a swap interface and a FRA interface. I just found unexpected behavior in the FRA results that I'm trying address. Also yield curve perturbations and then there are my 3 other clients. Sorry, another interface outside of fixed income is outside my ability to resource. Happy to advise. There's already an AmericanOption with a dividend yield, just add dividends and fork on non-zero dividend to call other quantlib dividend based method.

eddelbuettel commented 8 years ago

@student-t Let's see: maybe because I have a day job, over two dozen other CRAN packages several of which I released in the last few days, well over 100 CRAN packages I maintain for Debian where I currently prep for tomorrow's first beta of R 3.3.2, and maybe also because I occasionally sleep. So, your turn: Why don't you?

Jokes aside: easily contained and well-defined problems are the best to start so a case can be made that these should in fact be left as training ground for new contributors.

@tleitch Point taken. I haven't looked at the class interfaces but generalizing to 'with dividend' defaulting to a value of zero and giving old behaviour sounds good to me too.

hfty commented 8 years ago

@eddelbuettel I'm well aware of your enormous contributions to the community (and very grateful for them), and would love to do my part if I can wrap my head around the C++ side of things. I'm starting, as you suggested, from the unit test DividendOptionTest::testEuropeanKnownValue (doesn't look like there's a test unit for American options with discrete dividends). I'll try to add optional dividend parameters for AmericanOption and EuropeanOption, as suggested by @tleitch.

eddelbuettel commented 8 years ago

@hfty What I try in a case like that is to first builds a small self-contained program depending only on QuantLib. Maybe with the fewest feature. And then build up from there. What is in RQuantLib can serve as a framework for adding to it. And if you're stuck, come here and ask for help. No point in spinning the wheels...

tleitch commented 8 years ago

@hfty A great resource on the C++ side are the examples in Quantlib. Some of the RQuantlib functions are almost direct wraps of an example. If you know some C++ and compare an existing R/Rcpp wrap with it's matching example you can infer what your interface should look like. AmericanOption is a great start, you only need to figure out how to add the dividend input (it will be two data points, amount and time/date) and then figure out how to format that data for the method call that takes discrete dividends. The Module you want if FDDividendAmericanEngine which is related to FDAemeicanEngine. There is no example for the dividend version, but there is test suite code where it is called. I would hunt that down and compare it with the dividend yield version to help with design the call.

The outputs are the same, so no work needed there.

Best of luck. C++ is the easy part. Dirk is much harder to learn.

eddelbuettel commented 8 years ago

Dirk is easy.

Terry was referring to the fact that he also needed a crash course in how to deal with Git(Hub). :grinning:

tleitch commented 8 years ago

Too many years in a C suite..... ;~)

hfty commented 8 years ago

Thanks for the advice, @eddelbuettel and @tleitch. I'm sluggishly making my way through the code.

The engine expects vectors for dividend dates and amounts, and I'm not sure what the best way to pass those as parameters with Rcpp.

From the test suite:

std::vector<Date> dividendDates;
std::vector<Real> dividends;

Does that mean I can just add a std::vector<Real> dividends argument to the americanOptionEngine function, or should it be e.g. a Rcpp::NumericVector? I want it to be able to handle the default case where it's just a zero...

For the dividendDates, it's a little trickier given I think we should pass a vector of time to dividends, rather than actual dates, just like what you've done for the maturity. I guess the easiest would be to have a std::vector<Real> dividendTimes parameter (not sure about the name...), and then loop over it to create the std::vector<Date>?

I thought of making it a unique dividend for simplicity, but given the QuantLib function expects a vector anyway... might as well.

eddelbuettel commented 8 years ago

The fixed incomes should have similar things for coupon dates and payment. In essence you are correct: vectors of dates and values.

We do have Rcpp::Datevector, and that whole stack is a bit of mess in RQuantLib because it was all done before Rcpp had facilities for it. (For what it is worth I finally added something better; hopefully in the next Rcpp release.) But there should be plenty of working examples.

hfty commented 8 years ago

I see. I can use some of the syntax from the bonds file, thank you.

For dates, however, I guess it's a stylistic choice. Bond valuation uses actual dates, and then asks you for the effectiveDate at which to value the bond. The option functions in RQuantLib, however, abstract from that and ask you for a maturity (in years), then convert it to a Date from today. For consistency, I think it would be better to do the same for discrete dividends: ask for time (in years) to the dividend, then convert it into dates from today. I think I should be able to make that work.

eddelbuettel commented 8 years ago

Yes -- real valued 'time until' as fractional years is common. But that can happen both ways: giving QL a vector of (dividend) dates as well as a current evaluation date (today is a default), and letting it compute the time steps, or giving it the time steps. I usually follow the example and test code and try to conform to the existing class interfaces.

hfty commented 8 years ago

There is no example for this, but the unit test uses fractional years from today, so I'll try to go with that. Thanks!

hfty commented 8 years ago

Looks like I have a working prototype! Thank you again for your help. I did both the European and American version. However, a few questions before I submit a pull requests for your consideration:

1) There is a test for the European DividendVanillaOption in QuantLib, testEuropeanKnownValue. The test, however, is commented out with the message "Doesn't quite work. Need to deal with date conventions". I do get 3.67123 vs 3.6719 in the Hull (example 12.8), where it's from. But it's an upstream issue.

However, no such test for the American version, and the issue is that I would need to compare to a Finite Difference benchmark. Haug uses a discrete dividend yield model (i.e. % of the spot at the ex-dividend date, if I understood that right); Hull uses a binomial tree. The values I obtain are close to Haug, but not exactly the same; they are fairly different from those of Hull, although the Greeks are very similar.

I guess that it is not our job to check that the upstream results are correct, but I'd like the new functionality to be properly tested anyway...

2) I've added the extra parameters as a discreteDividends and discreteDividendsTimeUntil, vectors of type double, at the end of the existing parameters for both engine. It may be more logical to add them after dividendYield, but I worry about backward compatibility for people who use unnamed parameters. Do you have a suggestion on either the names or the order?

3) For American option, the "BaroneAdesiWhaley" does not handle discrete dividend, so if the engine is left on the default and not set to "CrankNicolson", I switch it and send a Rf_warning. Is that a good way to handle the case?

4) Should I worry about europeanOptionArraysEngine at this stage? (I don't think there is an americanOptionArraysEngine yet. Maybe something for me to do next?)

eddelbuettel commented 8 years ago

Sounds good! Now in order:

1) That sounds fine. Sometimes we just have a few decimals of precision. Over the years I even had to adjust test result comparison for the same routine (because of subtle implementation details, things that can happen with numerical optimization and alike and so on). So we could compare the European for up to, say, 3.67. American Options are trickier as is known so we may skip this, or just test 'ballpark'.

2) We have something new in Rcpp which can: a templated Nullable<> . I can update this once you have sent a pull request -- it is not something you need to do now. And yes, I would place them at the end as C++ cares about the order...

3) Seems right.

4) Naaa. That was an experiment a decade ago. Just leave as is.

hfty commented 8 years ago

PR merged! Thanks for your help.

eddelbuettel commented 8 years ago

Ahh. Forgot we had an open issue. Will mark.

(Pro-tip: Having (Closes #72) in your commit message automates this.)