Login  Register

Strange Basic Option Valuation Problem - Part II

Posted by UNG on Jan 16, 2006; 1:32pm
URL: http://quantlib.414.s1.nabble.com/Strange-Basic-Option-Valuation-Problem-Part-II-tp4357.html


Consider the following European option:

        Option::Type type(Option::Call);
        Real underlying = 30.07;
        Real strike = 35.0;
        Spread dividendYield = 0.0;
        Rate riskFreeRate = 0.0433;

        Date todaysDate(16, January, 2006);
        Date settlementDate(16, January, 2006);
        Date exerciseDate(18, February, 2006);

        DayCounter dayCounter = Actual360();
        double optPx = 0.55;

Compare results from:
 
** Option calculator
http://www.888options.com/resources/options_calc.jsp
(Bloomberg results are the same within 4th decimal)
I.V.  0.549 Delta 0.2105 Gamma 0.0576 Vega  0.0261
Rho   0.0052

** Quantlib:
impliedVol:     0.549073
delta:  0.0567826
gamma:  0.0417672
theta:  -0.00485107
vega:   0.0103857
rho:    0.150655

Attached is QuantLib default example with hardcoded
parameters.

---8<-----------------------------------------------------------------------------------------------------

/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil;
c-basic-offset: 4 -*- */

/*!
 Copyright (C) 2003 Ferdinando Ametrano
 Copyright (C) 2000, 2001, 2002, 2003 RiskMap srl

 This file is part of QuantLib, a
free-software/open-source library  for financial
quantitative analysts and developers -
http://quantlib.org/

 QuantLib is free software: you can redistribute it
and/or modify it  under the terms of the QuantLib
license.  You should have received a  copy of the
license along with this program; if not, please email
<[hidden email]>. The license is also
available online at
<http://quantlib.org/reference/license.html>.

 This program is distributed in the hope that it will
be useful, but WITHOUT  ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS  FOR A
PARTICULAR PURPOSE.  See the license for more details.
*/

#include <ql/quantlib.hpp>
#include <iostream>

using namespace QuantLib;

#if defined(QL_ENABLE_SESSIONS)
namespace QuantLib {

    Integer sessionId() { return 0; }

}
#endif


// This will be included in the library after a bit of
redesign class WeightedPayoff {
    public:
        WeightedPayoff(Option::Type type,
               Time maturity,
               Real strike,
               Real s0,
               Volatility sigma,
               Rate r,
               Rate q)
        : type_(type), maturity_(maturity),
        strike_(strike),
        s0_(s0),
        sigma_(sigma),r_(r), q_(q){}

        Real operator()(Real x) const {
           Real nuT =
(r_-q_-0.5*sigma_*sigma_)*maturity_;
           return std::exp(-r_*maturity_)
               *PlainVanillaPayoff(type_,
strike_)(s0_*std::exp(x))
               *std::exp(-(x - nuT)*(x
-nuT)/(2*sigma_*sigma_*maturity_))
             
/std::sqrt(2.0*M_PI*sigma_*sigma_*maturity_);
        }
private:
    Option::Type type_;
    Time maturity_;
    Real strike_;
    Real s0_;
    Volatility sigma_;
    Rate r_,q_;
};


int main(int, char* [])
{
    try {
        QL_IO_INIT

        std::cout << "Using " << QL_VERSION <<
std::endl << std::endl;

        // our option
        Option::Type type(Option::Call);
        Real underlying = 30.07; //50.78; //51.69;
        Real strike = 35.0; // 52.5;
        Spread dividendYield = 0.0; // 0.0070964;
//0.0;
        Rate riskFreeRate = 0.0433; //0.01;

        Date todaysDate(16, January, 2006);
        Date settlementDate(16, January, 2006);
        Settings::instance().evaluationDate() =
todaysDate;

        Date exerciseDate(18, February, 2006);
        DayCounter dayCounter = Actual360();
        Time maturity =
dayCounter.yearFraction(settlementDate,
                                               
exerciseDate);

        Volatility volatility = 0.3;
        std::cout << "option type = "  << type <<
std::endl;
        std::cout << "Time to maturity = "        <<
maturity
                  << std::endl;
        std::cout << "Underlying price = "        <<
underlying
                  << std::endl;
        std::cout << "Strike = "                  <<
strike
                  << std::endl;
        std::cout << "Risk-free interest rate = " <<
io::rate(riskFreeRate)
                  << std::endl;
        std::cout << "Dividend yield = " <<
io::rate(dividendYield)
                  << std::endl;
        std::cout << "Volatility = " <<
io::volatility(volatility)
                  << std::endl;
        std::cout << std::endl;

//        Date midlifeDate(19, November, 1998);
//        std::vector<Date> exDates(2);
//        exDates[0]=midlifeDate;
//        exDates[1]=exerciseDate;
//
        boost::shared_ptr<Exercise> exercise(
                                          new
EuropeanExercise(exerciseDate));
//        boost::shared_ptr<Exercise> amExercise(
//                                          new
AmericanExercise(settlementDate,
//                                                    
          exerciseDate));
//        boost::shared_ptr<Exercise> berExercise(new
BermudanExercise(exDates));
//
//
        Handle<Quote> underlyingH(
            boost::shared_ptr<Quote>(new
SimpleQuote(underlying)));

        // bootstrap the yield/dividend/vol curves
        Handle<YieldTermStructure> flatTermStructure(
            boost::shared_ptr<YieldTermStructure>(
                new FlatForward(settlementDate,
riskFreeRate, dayCounter)));
        Handle<YieldTermStructure> flatDividendTS(
            boost::shared_ptr<YieldTermStructure>(
                new FlatForward(settlementDate,
dividendYield, dayCounter)));
        Handle<BlackVolTermStructure> flatVolTS(
            boost::shared_ptr<BlackVolTermStructure>(
                new BlackConstantVol(settlementDate,
volatility, dayCounter))); //
//        std::vector<Date> dates(4);
//        dates[0] = settlementDate + 1*Months;
//        dates[1] = exerciseDate;
//        dates[2] = exerciseDate + 6*Months;
//        dates[3] = exerciseDate + 12*Months;
//        std::vector<Real> strikes(4);
//        strikes[0] = underlying*0.9;
//        strikes[1] = underlying;
//        strikes[2] = underlying*1.1;
//        strikes[3] = underlying*1.2;
//
//        Matrix vols(4,4);
//        vols[0][0] = volatility*1.1;
//                     vols[0][1] = volatility;
//                                  vols[0][2] =
volatility*0.9;
//                                              
vols[0][3] = volatility*0.8;
//        vols[1][0] = volatility*1.1;
//                     vols[1][1] = volatility;
//                                  vols[1][2] =
volatility*0.9;
//                                              
vols[1][3] = volatility*0.8;
//        vols[2][0] = volatility*1.1;
//                     vols[2][1] = volatility;
//                                  vols[2][2] =
volatility*0.9;
//                                              
vols[2][3] = volatility*0.8;
//        vols[3][0] = volatility*1.1;
//                     vols[3][1] = volatility;
//                                  vols[3][2] =
volatility*0.9;
//                                              
vols[3][3] = volatility*0.8;
//
//        Handle<BlackVolTermStructure> blackSurface(
//          
boost::shared_ptr<BlackVolTermStructure>(
//                new
BlackVarianceSurface(settlementDate, dates,
//                                         strikes,
vols, dayCounter)));


        boost::shared_ptr<StrikedTypePayoff>
payoff(new
            PlainVanillaPayoff(type, strike));

        boost::shared_ptr<BlackScholesProcess>
stochasticProcess(new
            BlackScholesProcess(underlyingH,
flatDividendTS,
                                flatTermStructure,
                                //  blackSurface
                                flatVolTS));

        EuropeanOption option(stochasticProcess,
payoff, exercise);


        std::string method;
        Real value, discrepancy, rightValue,
relativeDiscrepancy;

        std::cout << std::endl << std::endl;

        // write column headings
        std::cout <<
"Method\t\tValue\t\tEstimatedError\tDiscrepancy"
                     "\tRel. Discr." << std::endl;

        // method: Black-Scholes Engine
        method = "Black-Scholes";
       
option.setPricingEngine(boost::shared_ptr<PricingEngine>(
            new AnalyticEuropeanEngine()));
       
        double optPx = 0.55; //1.6; //1.75;
       
        double impliedVol = option.impliedVolatility(
optPx );        
        double delta = option.delta();
        double gamma = option.gamma();
        double theta = option.theta()/365.0;
        double vega = option.vega()/100.0;
        double rho = option.rho();
        double divRho = option.dividendRho();

        std::cout << "impliedVol: " << "\t" <<
impliedVol << std::endl
            << "delta: " << "\t" << delta << std::endl
            << "gamma: " << "\t" << gamma << std::endl
            << "theta: " << "\t" << theta << std::endl
            << "vega: " << "\t" << vega << std::endl
            << "rho: " << "\t" << rho << std::endl
            << "divRho: " << "\t" << divRho <<
std::endl;


        rightValue = value = option.NPV();
        discrepancy = std::fabs(value-rightValue);
        relativeDiscrepancy = discrepancy/rightValue;
        std::cout << method << "\t"
                  << value << "\t" << "N/A\t\t"
                  << discrepancy << "\t\t" <<
relativeDiscrepancy << std::endl;


        // method: Integral
        method = "Integral";
       
option.setPricingEngine(boost::shared_ptr<PricingEngine>(
            new IntegralEngine()));
        value = option.NPV();
        discrepancy = std::fabs(value-rightValue);
        relativeDiscrepancy = discrepancy/rightValue;
        std::cout << method << "\t"
                  << value << "\t" << "N/A\t\t"
                  << discrepancy << "\t" <<
relativeDiscrepancy << std::endl;

/*
        // method: Integral
        method = "Binary Cash";
       
option.setPricingEngine(boost::shared_ptr<PricingEngine>(
            new IntegralCashOrNothingEngine(1.0)));
        value = option.NPV();
        discrepancy = std::fabs(value-rightValue);
        relativeDiscrepancy = discrepancy/rightValue;
        std::cout << method << "\t"
                  << value << "\t" << "N/A\t\t"
                  << discrepancy << "\t" <<
relativeDiscrepancy << std::endl;

        // method: Integral
        method = "Binary Asset";
       
option.setPricingEngine(boost::shared_ptr<PricingEngine>(
            new IntegralAssetOrNothingEngine()));
        value = option.NPV();
        discrepancy = std::fabs(value-rightValue);
        relativeDiscrepancy = discrepancy/rightValue;
        std::cout << method << "\t"
                  << value << "\t" << "N/A\t\t"
                  << discrepancy << "\t" <<
relativeDiscrepancy << std::endl;

*/
        Size timeSteps = 801;

        // Binomial Method (JR)
        method = "Binomial (JR)";
       
option.setPricingEngine(boost::shared_ptr<PricingEngine>(
            new
BinomialVanillaEngine<JarrowRudd>(timeSteps)));
        value = option.NPV();
        discrepancy = std::fabs(value-rightValue);
        relativeDiscrepancy = discrepancy/rightValue;
        std::cout << method << "\t"
                  << value << "\t" << "N/A\t\t"
                  << discrepancy << "\t" <<
relativeDiscrepancy << std::endl;


        // Binomial Method (CRR)
        method = "Binomial (CRR)";
       
option.setPricingEngine(boost::shared_ptr<PricingEngine>(
            new
BinomialVanillaEngine<CoxRossRubinstein>(timeSteps)));
        value = option.NPV();
        discrepancy = std::fabs(value-rightValue);
        relativeDiscrepancy = discrepancy/rightValue;
        std::cout << method << "\t"
                  << value << "\t" << "N/A\t\t"
                  << discrepancy << "\t" <<
relativeDiscrepancy << std::endl;

        // Equal Probability Additive Binomial Tree
(EQP)
        method = "Additive (EQP)";
       
option.setPricingEngine(boost::shared_ptr<PricingEngine>(
            new
BinomialVanillaEngine<AdditiveEQPBinomialTree>(timeSteps)));
        value = option.NPV();
        discrepancy = std::fabs(value-rightValue);
        relativeDiscrepancy = discrepancy/rightValue;
        std::cout << method << "\t"
                  << value << "\t" << "N/A\t\t"
                  << discrepancy << "\t" <<
relativeDiscrepancy << std::endl;

        // Equal Jumps Additive Binomial Tree
(Trigeorgis)
        method = "Bin. Trigeorgis";
       
option.setPricingEngine(boost::shared_ptr<PricingEngine>(
            new
BinomialVanillaEngine<Trigeorgis>(timeSteps)));
        value = option.NPV();
        discrepancy = std::fabs(value-rightValue);
        relativeDiscrepancy = discrepancy/rightValue;
        std::cout << method << "\t"
                  << value << "\t" << "N/A\t\t"
                  << discrepancy << "\t" <<
relativeDiscrepancy << std::endl;

        // Tian Binomial Tree (third moment matching)
        method = "Binomial Tian";
       
option.setPricingEngine(boost::shared_ptr<PricingEngine>(
            new
BinomialVanillaEngine<Tian>(timeSteps)));
        value = option.NPV();
        discrepancy = std::fabs(value-rightValue);
        relativeDiscrepancy = discrepancy/rightValue;
        std::cout << method << "\t"
                  << value << "\t" << "N/A\t\t"
                  << discrepancy << "\t" <<
relativeDiscrepancy << std::endl;

        // Leisen-Reimer Binomial Tree
        method = "Binomial LR";
       
option.setPricingEngine(boost::shared_ptr<PricingEngine>(
            new
BinomialVanillaEngine<LeisenReimer>(timeSteps)));
        value = option.NPV();
        discrepancy = std::fabs(value-rightValue);
        relativeDiscrepancy = discrepancy/rightValue;
        std::cout << method << "\t"
                  << value << "\t" << "N/A\t\t"
                  << discrepancy << "\t" <<
relativeDiscrepancy << std::endl;

        // Finite Differences

        method = "Finite Diff.";
        timeSteps = 100;
        Size gridPoints = 100;
       
option.setPricingEngine(boost::shared_ptr<PricingEngine>(
            new FDEuropeanEngine(timeSteps,
gridPoints)));
        value = option.NPV();
        discrepancy = std::fabs(value-rightValue);
        relativeDiscrepancy = discrepancy/rightValue;
        std::cout << method << "\t"
                  << value << "\t" << "N/A\t\t"
                  << discrepancy << "\t" <<
relativeDiscrepancy << std::endl;

        // Monte Carlo Method
        timeSteps = 1;

        method = "MC (crude)";
        Size mcSeed = 42;

        boost::shared_ptr<PricingEngine> mcengine1;
        mcengine1 =
           
MakeMCEuropeanEngine<PseudoRandom>().withSteps(timeSteps)
                                               
.withTolerance(0.02)
                                               
.withSeed(mcSeed);
        option.setPricingEngine(mcengine1);

        value = option.NPV();
        Real errorEstimate = option.errorEstimate();
        discrepancy = std::fabs(value-rightValue);
        relativeDiscrepancy = discrepancy/rightValue;
        std::cout << method << "\t"
                  << value << "\t" << errorEstimate <<
"\t"
                  << discrepancy << "\t" <<
relativeDiscrepancy << std::endl;

        method = "MC (Sobol)";
        timeSteps = 1;
        Size nSamples = 32768;  // 2^15

        boost::shared_ptr<PricingEngine> mcengine2;
        mcengine2 =
           
MakeMCEuropeanEngine<LowDiscrepancy>().withSteps(timeSteps)
                                                 
.withSamples(nSamples);
        option.setPricingEngine(mcengine2);

        value = option.NPV();
        discrepancy = std::fabs(value-rightValue);
        relativeDiscrepancy = discrepancy/rightValue;
        std::cout << method << "\t"
                  << value << "\t" << "N/A\t\t"
                  << discrepancy << "\t" <<
relativeDiscrepancy << std::endl;

        return 0;
    } catch (std::exception& e) {
        std::cout << e.what() << std::endl;
        return 1;
    } catch (...) {
        std::cout << "unknown error" << std::endl;
        return 1;
    }
}

---8<-----------------------------------------------------------------------------------------------------