Pricing Binomial Options with Fractional-Year Maturities in QuantLib
Summary
The document explains how to use QuantLib’s Cox–Ross–Rubinstein binomial approach when maturity is specified as a fraction of a year rather than by calendar dates. The questioner’s code constructs the process and lattice using a numeric maturity but gets stuck creating the discretized option, whose exercise schedule relies on dates. The accepted response describes a working approach for an American option: choose a start date, convert the year fraction to a number of days using a day-count convention, and set the exercise date accordingly.
A second answer confirms that QuantLib’s date-oriented machinery requires using dates and recommends a simple day counter such as Actual/360 to make the conversion straightforward. The example demonstrates one implementation, including a particular day-count basis and rounding method. It is a practical workaround rather than a date-independent binomial pricing interface; other day-count conventions or rounding choices can map the same year fraction to a different date and effective maturity. The original question concerns a European option, while the working example prices an American option, so details may need adapting.
Key ideas
- QuantLib’s binomial option workflow relies on exercise dates even when model maturity is supplied as a year fraction.
- A practical workaround is to create a start date and derive the exercise date with a day-count convention.
- Actual/360 provides a direct mapping from a fraction of a year to a number of days.
- The example’s date conversion and rounding choices affect the effective maturity.
- The accepted code example prices an American option, so adapting it to a European exercise schedule may be necessary.
Tags
Full text
# Using the binomial-tree approach to price an option in quantlib - with time expressed as a fraction of year
# Using the binomial-tree approach to price an option in quantlib - with time expressed as a fraction of year
For learning purpose, I'm trying to price, with quantlib, an European option using the Cox-Ross-Rubinstein tree approach.
Some examples are provided in the file `EquityOption.cpp` that is part of the library.
However I would like to price it, without using dates (that is to say, without any day convention, calendar, expiration date ...) but using only a fraction of year as maturity (ex : with `T = 0.5` ).
> As far I read, all examples only consider the case with dates expressed as "real" dates (ex : 11/01/2016) and not expressed as time to maturity (ex: T= 0.1), i.e. the standard textbook case.
Since the library relies heavily on engines, and that those engines are mainly designed with dates, I have some difficulties to get rid off the dates.
For the BS analytical formula, it is easy since the core of the engine relies on the BlackScholesCalculator that is dates independent (see a full example here) .
However for the binomial case, there is no such date-independent routine (no BinomialCalculator..), and the `DiscretizedVanillaOption` class requires an engine's variable as input to instantiate it..
One plausible solution that came to my mind is to find two "fakes" dates, by playing with a pseudo `.yearFraction(FakeStartDate, FakeEndDate);` function to find two optimal dates that match the fraction I want. Then to use these two dates to compute the price. However this strategy seems quiet cumbersome for a such simple request.
How can I compute the value of an option using the binomial approach with a maturity expressed as a fraction of year in quantlib ?
Please see below what I have done so far, I'm stuck to the line that instantiate the `DiscretizedVanillaOption`.
```
Calendar calendar = NullCalendar();
DayCounter dayCounter = SimpleDayCounter();
Date t0(1);
Option::Type type = Option::Put;
Real S0 = spot_;
Real K = strike_;
Spread q = dividendYield_;
Rate r = interestRate_;
Volatility sigma = volatility_;
Settings::instance().evaluationDate() = t0;
Handle<Quote> underlyingH((boost::static_pointer_cast<Quote>(boost::make_shared<SimpleQuote>(S0))));
Handle<YieldTermStructure> flatDividendTS((boost::static_pointer_cast<YieldTermStructure>(boost::make_shared<FlatForward>(t0, q, dayCounter))));
Handle<YieldTermStructure> flatTermStructure((boost::static_pointer_cast<YieldTermStructure>(boost::make_shared<FlatForward>(t0, r, dayCounter))));
Handle<BlackVolTermStructure> flatVolTS((boost::static_pointer_cast<BlackVolTermStructure>(boost::make_shared<BlackConstantVol>(t0, calendar, sigma, dayCounter))));
boost::shared_ptr<BlackScholesMertonProcess> bsmProcess(new BlackScholesMertonProcess(underlyingH, flatDividendTS, flatTermStructure, flatVolTS));
boost::shared_ptr<StrikedTypePayoff> payoff(new PlainVanillaPayoff(type, K));
Size timeSteps_ = 200;
Time maturity = 0.2;
TimeGrid grid(maturity, timeSteps_);
boost::shared_ptr<CoxRossRubinstein> tree(new CoxRossRubinstein(bsmProcess, maturity, timeSteps_, payoff->strike()));
boost::shared_ptr<BlackScholesLattice<CoxRossRubinstein> > lattice( new BlackScholesLattice<CoxRossRubinstein>(tree, r, maturity, timeSteps_));
DiscretizedVanillaOption option(/* ????*/,bsmProcess, grid);//<--------- First argument Here ?
option.initialize(lattice, maturity);
double price = option.presentValue();
```
## Answer by Malick (score 2, accepted)
https://quant.stackexchange.com/a/32715
Finally, thanks to Luigi's answer and by observing the examples in the `testsuite` I have been able to achieve it. The fake starting date is setted to today's date and the exercise date as follow :
```
Date exDate = today + Integer(timeToMaturity_*360+0.5);
```
See below a working example for pricing an american option with CRR :
```
double American_CoxRossRubinstein_T(bool IsCall /*if false= put*/,
double underlying_,
double strike_,
double riskfree_,
double volatility_,
double dividendYield_,
double timeToMaturity_)
{
/*----------------------------------------------------*/
Option::Type optionType =IsCall ? Option::Call : Option::Put ;
QuantLib::Date today = QuantLib::Date::todaysDate();
QuantLib::Settings::instance().evaluationDate() = today;
QuantLib::DayCounter dayCounter = QuantLib::Actual360();
Calendar calendar = NullCalendar();
Date exDate = today + Integer(timeToMaturity_*360+0.5);
Handle<Quote> underlyingH((boost::static_pointer_cast<Quote>(boost::make_shared<SimpleQuote>(underlying_))));
Handle<YieldTermStructure> flatDividendTS((boost::static_pointer_cast<YieldTermStructure>(boost::make_shared<FlatForward>(today, dividendYield_, dayCounter))));
Handle<YieldTermStructure> flatTermStructure((boost::static_pointer_cast<YieldTermStructure>(boost::make_shared<FlatForward>(today,riskfree_, dayCounter))));
Handle<BlackVolTermStructure> flatVolTS((boost::static_pointer_cast<BlackVolTermStructure>(boost::make_shared<BlackConstantVol>(today, calendar, volatility_, dayCounter))));
boost::shared_ptr<Exercise> americanExercise(new AmericanExercise(today, exDate));
boost::shared_ptr<StrikedTypePayoff> payoff(new PlainVanillaPayoff(optionType, strike_));
VanillaOption americanOption(payoff, americanExercise);
boost::shared_ptr<BlackScholesMertonProcess> bsmProcess(new BlackScholesMertonProcess(underlyingH, flatDividendTS, flatTermStructure, flatVolTS));
Size timeSteps = static_cast<int>(std::max(700 * timeToMaturity_, 20.0));
americanOption.setPricingEngine(boost::static_pointer_cast<PricingEngine>(boost::make_shared<BinomialVanillaEngine<CoxRossRubinstein>>(bsmProcess, timeSteps )));
Real OptionPrice = americanOption.NPV();
std::cout << std::setprecision(9) << "American_CoxRossRubinstein_T = " << OptionPrice << std::endl;
return OptionPrice;
}
```
## Answer by Luigi Ballabio (score 4)
https://quant.stackexchange.com/a/32564
I'm afraid you'll have to use fake dates, as you mentioned.
To make the process less cumbersome, you can use a day counter with a simple formula such as Act/360. Given a start date, it will make it easier to determine the corresponding end date (`startDate+36` for T=0.1, `startDate+180` for T=0.5 and so on) without having to play with candidates.Shown in full with attribution under the source's licence. Licence: CC BY-SA 4.0 (Stack Exchange)
This summary was written by Stratmill's research agent from the original; it is not a copy of the source.