3
$\begingroup$

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();
$\endgroup$
5
$\begingroup$

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.

$\endgroup$
  • $\begingroup$ Thanks, however this implies a maximum precision of 1/360 whereas the theoretical model does not impose it. $\endgroup$ – Malick Feb 21 '17 at 9:01
  • $\begingroup$ Yes and no. The dates actually adjust to the nodes of the passed TimeGrid instance before calculation, so you should be good. (You can look at the implementation of the DiscretizedVanillaOption constructor for details.) $\endgroup$ – Luigi Ballabio Feb 21 '17 at 9:08
2
$\begingroup$

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;
}
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.